diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 62af3b6c62ce733b0b94413e76e24b991cab9642..ca29dd1f8ce93b492f20df230c28e2b10a742115 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -968,6 +968,27 @@ const options = [
     mergeable: true,
     cli: false,
   },
+  {
+    name: 'pip_requirements',
+    description: 'Configuration object for requirements.txt files',
+    parentManager: 'python',
+    state: 'repository',
+    type: 'json',
+    default: {
+      enabled: false,
+    },
+    mergeable: true,
+    cli: false,
+  },
+  {
+    name: 'python',
+    description: 'Configuration object for python',
+    state: 'repository',
+    type: 'json',
+    default: {},
+    mergeable: true,
+    cli: false,
+  },
 ];
 
 function getOptions() {
diff --git a/lib/manager/index.js b/lib/manager/index.js
index 2c083425b6aab1416d62fddd773ff542660697db..7869af56b5bdfee112371e4eb7e553696fd0a2f6 100644
--- a/lib/manager/index.js
+++ b/lib/manager/index.js
@@ -12,6 +12,7 @@ const managerList = [
   'meteor',
   'npm',
   'nvm',
+  'pip_requirements',
   'travis',
 ];
 for (const manager of managerList) {
diff --git a/lib/manager/pip_requirements/extract.js b/lib/manager/pip_requirements/extract.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d55b22e63c03ca0c5ae3913b516546e6515cd83
--- /dev/null
+++ b/lib/manager/pip_requirements/extract.js
@@ -0,0 +1,32 @@
+// based on https://www.python.org/dev/peps/pep-0508/#names
+const packagePattern = '([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])';
+
+module.exports = {
+  packagePattern,
+  extractDependencies,
+};
+
+function extractDependencies(fileContent) {
+  logger.debug('pip_requirements.extractDependencies()');
+  // TODO: for now we only support semver, but we need better support for python versions
+  // see https://github.com/pypa/packaging/blob/master/packaging/version.py
+  // see https://www.python.org/dev/peps/pep-0440/#version-epochs
+  const regex = new RegExp(
+    `^${packagePattern}==([0-9]+\\.[0-9]+\\.[0-9]+)$`,
+    'g'
+  );
+  return fileContent
+    .split('\n')
+    .map((line, lineNumber) => {
+      const matches = regex.exec(line);
+      return (
+        matches && {
+          depName: matches[1],
+          depType: 'python',
+          currentVersion: matches[2],
+          lineNumber,
+        }
+      );
+    })
+    .filter(Boolean);
+}
diff --git a/lib/manager/pip_requirements/index.js b/lib/manager/pip_requirements/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..a405b6d489411cb033f611f2976c023eb4c4b963
--- /dev/null
+++ b/lib/manager/pip_requirements/index.js
@@ -0,0 +1,16 @@
+const { packagePattern, extractDependencies } = require('./extract');
+const { getPackageUpdates } = require('./package');
+const { updateDependency } = require('./update');
+
+const filePattern = /(^|\/)([\w-]*)requirements.(txt|pip)$/;
+const contentPattern = new RegExp(`^${packagePattern}==`);
+const parentManager = 'python';
+
+module.exports = {
+  contentPattern,
+  extractDependencies,
+  filePattern,
+  getPackageUpdates,
+  parentManager,
+  updateDependency,
+};
diff --git a/lib/manager/pip_requirements/package.js b/lib/manager/pip_requirements/package.js
new file mode 100644
index 0000000000000000000000000000000000000000..b922eb615b4b3f3494b11a970b8d7473180a7b42
--- /dev/null
+++ b/lib/manager/pip_requirements/package.js
@@ -0,0 +1,49 @@
+const got = require('got');
+const {
+  isGreaterThan,
+  semverSort,
+  isPinnedVersion,
+  getMajor,
+} = require('../../util/semver');
+
+module.exports = {
+  getPackageUpdates,
+};
+
+async function getPackageUpdates(config) {
+  try {
+    logger.debug('pip_requirements.getPackageUpdates()');
+    const { currentVersion, depName } = config;
+    if (!isPinnedVersion(currentVersion)) {
+      return [];
+    }
+    const { releases } = (await got(`https://pypi.org/pypi/${depName}/json`, {
+      json: true,
+    })).body;
+    const newVersions = Object.keys(releases)
+      .filter(
+        release =>
+          isPinnedVersion(release) && isGreaterThan(release, currentVersion)
+      )
+      .sort(semverSort);
+
+    if (newVersions.length) {
+      logger.info({ newVersions, depName }, 'Found newer Python releases');
+    } else {
+      return [];
+    }
+
+    const newVersion = newVersions.pop();
+
+    return [
+      {
+        depName,
+        newVersion,
+        newVersionMajor: getMajor(newVersion),
+      },
+    ];
+  } catch (err) {
+    logger.info({ err }, 'Error fetching new package versions');
+    return [];
+  }
+}
diff --git a/lib/manager/pip_requirements/update.js b/lib/manager/pip_requirements/update.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8b601d5fdb242e70781a208579a2695748a477d
--- /dev/null
+++ b/lib/manager/pip_requirements/update.js
@@ -0,0 +1,15 @@
+module.exports = {
+  updateDependency,
+};
+
+function updateDependency(fileContent, upgrade) {
+  try {
+    logger.debug(`pip_requirements.updateDependency(): ${upgrade.newVersion}`);
+    const lines = fileContent.split('\n');
+    lines[upgrade.lineNumber] = `${upgrade.depName}==${upgrade.newVersion}`;
+    return lines.join('\n');
+  } catch (err) {
+    logger.info({ err }, 'Error setting new package version');
+    return null;
+  }
+}
diff --git a/test/_fixtures/pip_requirements/requirements.txt b/test/_fixtures/pip_requirements/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..05b90ecb5b381262641018b55753218403d245c6
--- /dev/null
+++ b/test/_fixtures/pip_requirements/requirements.txt
@@ -0,0 +1,5 @@
+--index-url http://example.com/private-pypi/
+# simple comment
+some-package==0.3.1
+not_semver==1.9
+
diff --git a/test/config/__snapshots__/index.spec.js.snap b/test/config/__snapshots__/index.spec.js.snap
index 4a04672a6e38f3d9d909afcbafb984ff54d04d15..fec7058639247fde2346c9ebf3bf158f581c4b4a 100644
--- a/test/config/__snapshots__/index.spec.js.snap
+++ b/test/config/__snapshots__/index.spec.js.snap
@@ -164,6 +164,9 @@ Object {
   },
   "pinDigests": true,
   "pinVersions": false,
+  "pip_requirements": Object {
+    "enabled": false,
+  },
   "platform": "github",
   "prBody": "This Pull Request {{#if isRollback}}rolls back{{else}}updates{{/if}} dependency {{#if repositoryUrl}}[{{{depName}}}]({{{repositoryUrl}}}){{else}}\`{{{depName}}}\`{{/if}} from \`{{#unless isRange}}{{#unless isPin}}v{{/unless}}{{/unless}}{{{currentVersion}}}\` to \`{{#unless isRange}}v{{/unless}}{{{newVersion}}}\`{{#if isRollback}}. This is necessary and important because \`v{{{currentVersion}}}\` cannot be found in the npm registry - probably because of it being unpublished.{{/if}}\\n{{#if hasTypes}}\\n\\nThis PR also includes an upgrade to the corresponding [@types/{{{depName}}}](https://npmjs.com/package/@types/{{{depName}}}) package.\\n{{/if}}\\n{{#if releases.length}}\\n\\n{{#if schedule}}\\n**Note**: This PR was created on a configured schedule (\\"{{{schedule}}}\\"{{#if timezone}} in timezone \`{{{timezone}}}\`{{/if}}) and will not receive updates outside those times.\\n{{/if}}\\n\\n{{#if isPin}}\\n**Important**: Renovate will wait until you have merged this Pin request before creating PRs for any *upgrades*. If you do not wish to pin anything, please update your config accordingly instead of leaving this PR open.\\n{{/if}}\\n{{#if hasReleaseNotes}}\\n\\n<details>\\n<summary>Release Notes</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.releaseNotes}}\\n### [\`v{{{release.version}}}\`]({{{release.releaseNotes.url}}})\\n\\n{{{release.releaseNotes.body}}}\\n\\n---\\n\\n{{/if}}\\n{{/each}}\\n</details>\\n{{/if}}\\n\\n{{#if hasCommits}}\\n\\n<details>\\n<summary>Commits</summary>\\n\\n{{#each releases as |release|}}\\n{{#if release.hasCommits}}\\n#### v{{{release.version}}}\\n{{#each release.commits as |commit|}}\\n-   [\`{{commit.shortSha}}\`]({{commit.url}}) {{commit.message}}\\n{{/each}}\\n{{/if}}\\n{{/each}}\\n\\n</details>\\n{{/if}}\\n{{/if}}\\n\\n{{#if hasErrors}}\\n\\n---\\n\\n# Errors\\n\\nRenovate encountered some errors when processing your repository, so you are being notified here even if they do not directly apply to this PR.\\n\\n{{#each errors as |error|}}\\n-   \`{{error.depName}}\`: {{error.message}}\\n{{/each}}\\n{{/if}}\\n\\n{{#if hasWarnings}}\\n\\n---\\n\\n# Warnings\\n\\nPlease make sure the following warnings are safe to ignore:\\n\\n{{#each warnings as |warning|}}\\n-   \`{{warning.depName}}\`: {{warning.message}}\\n{{/each}}\\n{{/if}}",
   "prConcurrentLimit": 0,
@@ -173,6 +176,7 @@ Object {
   "prNotPendingHours": 25,
   "prTitle": null,
   "privateKey": null,
+  "python": Object {},
   "rebaseStalePrs": null,
   "recreateClosed": false,
   "renovateFork": false,
diff --git a/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..f54547d2ec5c617261b0bd355f25b59c65117eff
--- /dev/null
+++ b/test/manager/pip_requirements/__snapshots__/extract.spec.js.snap
@@ -0,0 +1,12 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`lib/manager/pip_requirements/extract extractDependencies() extracts dependencies 1`] = `
+Array [
+  Object {
+    "currentVersion": "0.3.1",
+    "depName": "some-package",
+    "depType": "python",
+    "lineNumber": 2,
+  },
+]
+`;
diff --git a/test/manager/pip_requirements/__snapshots__/package.spec.js.snap b/test/manager/pip_requirements/__snapshots__/package.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..134fbfb3006787a6e852f41893859a78c01bf081
--- /dev/null
+++ b/test/manager/pip_requirements/__snapshots__/package.spec.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`lib/manager/pip_requirements/package getPackageUpdates() returns one upgrade 1`] = `
+Array [
+  Object {
+    "depName": "some-package",
+    "newVersion": "1.0.2",
+    "newVersionMajor": 1,
+  },
+]
+`;
diff --git a/test/manager/pip_requirements/extract.spec.js b/test/manager/pip_requirements/extract.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a775ba5644c486386d3ab2783a870c4e31cc573d
--- /dev/null
+++ b/test/manager/pip_requirements/extract.spec.js
@@ -0,0 +1,23 @@
+const fs = require('fs');
+const {
+  extractDependencies,
+} = require('../../../lib/manager/pip_requirements/extract');
+
+const requirements = fs.readFileSync(
+  'test/_fixtures/pip_requirements/requirements.txt',
+  'utf8'
+);
+
+describe('lib/manager/pip_requirements/extract', () => {
+  describe('extractDependencies()', () => {
+    let config;
+    beforeEach(() => {
+      config = {};
+    });
+    it('extracts dependencies', () => {
+      const res = extractDependencies(requirements, config);
+      expect(res).toMatchSnapshot();
+      expect(res).toHaveLength(1);
+    });
+  });
+});
diff --git a/test/manager/pip_requirements/package.spec.js b/test/manager/pip_requirements/package.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a727e0e7541bbf1e1421a27b754a84844815c9d1
--- /dev/null
+++ b/test/manager/pip_requirements/package.spec.js
@@ -0,0 +1,59 @@
+const {
+  getPackageUpdates,
+} = require('../../../lib/manager/pip_requirements/package');
+
+const defaultConfig = require('../../../lib/config/defaults').getConfig();
+const got = require('got');
+
+jest.mock('got');
+
+describe('lib/manager/pip_requirements/package', () => {
+  describe('getPackageUpdates()', () => {
+    let config;
+    beforeEach(() => {
+      config = {
+        ...defaultConfig,
+      };
+    });
+    it('returns empty on error', async () => {
+      config.depName = 'some-package';
+      config.currentVersion = '1.0.0';
+      expect(await getPackageUpdates(config)).toEqual([]);
+    });
+    it('returns empty if current version is not semver', async () => {
+      config.depName = 'some-package';
+      config.currentVersion = 'abcdefg';
+      expect(await getPackageUpdates(config)).toEqual([]);
+    });
+    it('returns empty if no newer versions', async () => {
+      config.depName = 'some-package';
+      config.currentVersion = '0.3.2';
+      got.mockReturnValueOnce({
+        body: {
+          releases: {
+            '0.2.0': [],
+            '0.3.2': [],
+          },
+        },
+      });
+      expect(await getPackageUpdates(config)).toEqual([]);
+    });
+    it('returns one upgrade', async () => {
+      config.depName = 'some-package';
+      config.currentVersion = '0.3.2';
+      got.mockReturnValueOnce({
+        body: {
+          releases: {
+            '0.2': [],
+            '0.2.0': [],
+            '0.3.2': [],
+            '0.3.4': [],
+            '1.0.0': [],
+            '1.0.2': [],
+          },
+        },
+      });
+      expect(await getPackageUpdates(config)).toMatchSnapshot();
+    });
+  });
+});
diff --git a/test/manager/pip_requirements/update.spec.js b/test/manager/pip_requirements/update.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..3e60f3109006a0371752aa19c2a08ae10adc1ed1
--- /dev/null
+++ b/test/manager/pip_requirements/update.spec.js
@@ -0,0 +1,28 @@
+const fs = require('fs');
+const {
+  updateDependency,
+} = require('../../../lib/manager/pip_requirements/update');
+
+const requirements = fs.readFileSync(
+  'test/_fixtures/pip_requirements/requirements.txt',
+  'utf8'
+);
+
+describe('manager/pip_requirements/update', () => {
+  describe('updateDependency', () => {
+    it('replaces existing value', () => {
+      const upgrade = {
+        depName: 'url',
+        lineNumber: 2,
+        newVersion: '1.0.1',
+      };
+      const res = updateDependency(requirements, upgrade);
+      expect(res).not.toEqual(requirements);
+      expect(res.includes(upgrade.newVersion)).toBe(true);
+    });
+    it('returns null if error', () => {
+      const res = updateDependency(null, null);
+      expect(res).toBe(null);
+    });
+  });
+});
diff --git a/website/docs/_posts/2017-10-05-configuration-options.md b/website/docs/_posts/2017-10-05-configuration-options.md
index 7ae7b7221fcbf476fdc164f881adcf6505f89c07..81317836153fde218c0d56fcd1c5a77622a178ba 100644
--- a/website/docs/_posts/2017-10-05-configuration-options.md
+++ b/website/docs/_posts/2017-10-05-configuration-options.md
@@ -908,6 +908,15 @@ Whether to convert ranged versions in `package.json` to pinned versions.
 
 This is a very important feature to consider, because not every repository's requirements are the same. The default value within the tool itself is false, which means no existing ranges are pinned. However if you are using the suggested preset `"config:base"`, then it changes the default of pinVersions to `null`, which means Renovate attempts to autodetect what's best for the project. In such cases `devDependencies` in `package.json` will alway be pinned, but `dependencies` will only be pinned if the package is `private` or has no `main` entry defined - both indicators that it is not intended to be published and consumed by other packages. To override the `"config:base"` setting, add the preset `":preserveSemverRanges"` to your `extends` array.
 
+## pip_requirements
+
+Configuration specific for requirements.txt updates.
+
+| name    | value              |
+| ------- | ------------------ |
+| type    | object             |
+| default | { enabled: false } |
+
 ## prBody
 
 Pull Request body template.
@@ -987,6 +996,15 @@ Pull Request title template
 
 The PR title is important for some of Renovate's matching algorithms (e.g. determining whether to recreate a PR or not) so ideally don't modify it much.
 
+## python
+
+Configuration specific for python updates.
+
+| name    | value             |
+| ------- | ----------------- |
+| type    | object            |
+| default | { enabled: true } |
+
 ## rebaseStalePrs
 
 Whether to rebase branches that are no longer up-to-date with the base branch.