From 9a32f35ddcdb342dade5acea94b52a6e420df815 Mon Sep 17 00:00:00 2001 From: praveshtora <pravesh.tora@gmail.com> Date: Mon, 22 Jul 2019 10:50:53 +0530 Subject: [PATCH] fix(pip_setup): handle updating when multiple deps per line (#4119) --- lib/manager/pip_requirements/update.js | 24 ++- .../__snapshots__/update.spec.js.snap | 177 ++++++++++++++++++ test/manager/pip_requirements/update.spec.js | 35 ++++ test/manager/pip_setup/_fixtures/setup-2.py | 81 ++++++++ 4 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 test/manager/pip_setup/_fixtures/setup-2.py diff --git a/lib/manager/pip_requirements/update.js b/lib/manager/pip_requirements/update.js index 0c8783352f..ba1ad061cb 100644 --- a/lib/manager/pip_requirements/update.js +++ b/lib/manager/pip_requirements/update.js @@ -10,10 +10,28 @@ function updateDependency(fileContent, upgrade) { logger.debug(`pip_requirements.updateDependency(): ${upgrade.newValue}`); const lines = fileContent.split('\n'); const oldValue = lines[upgrade.lineNumber]; - const newValue = oldValue.replace( - new RegExp(dependencyPattern), - `$1$2${upgrade.newValue}` + let newValue; + const multiDependencyRegex = new RegExp( + `(install_requires\\s*[=]\\s*\\[.*)(${upgrade.depName}.+?(?='))(.*])`, + 'g' ); + const multipleDependencyMatch = multiDependencyRegex.exec(oldValue); + if (multipleDependencyMatch) { + const dependency = multipleDependencyMatch[2]; + const updatedDependency = dependency.replace( + new RegExp(dependencyPattern), + `$1$2${upgrade.newValue}` + ); + newValue = oldValue.replace( + multiDependencyRegex, + `$1${updatedDependency}$3` + ); + } else { + newValue = oldValue.replace( + new RegExp(dependencyPattern), + `$1$2${upgrade.newValue}` + ); + } lines[upgrade.lineNumber] = newValue; return lines.join('\n'); } catch (err) { diff --git a/test/manager/pip_requirements/__snapshots__/update.spec.js.snap b/test/manager/pip_requirements/__snapshots__/update.spec.js.snap index 5d21201e80..be49d8f76c 100644 --- a/test/manager/pip_requirements/__snapshots__/update.spec.js.snap +++ b/test/manager/pip_requirements/__snapshots__/update.spec.js.snap @@ -1,5 +1,182 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`manager/pip_requirements/update updateDependency handles dependencies in different lines in setup.py 1`] = ` +" +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +setup( + author='Simon Davy', + author_email='simon.davy@canonical.com', + classifiers=[ + 'License :: OSI Approved :: Apache Software License', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Topic :: Internet :: WWW/HTTP :: WSGI', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', + 'Topic :: System :: Logging', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + ], + description='A common WSGI stack', + entry_points=dict( + console_scripts=[ + 'talisker=talisker:run_gunicorn', + 'talisker.run=talisker:run', + 'talisker.gunicorn=talisker:run_gunicorn', + 'talisker.gunicorn.eventlet=talisker:run_gunicorn_eventlet', + 'talisker.gunicorn.gevent=talisker:run_gunicorn_gevent', + 'talisker.celery=talisker:run_celery', + ], + ), + extras_require=dict( + celery=[ + 'celery>=3.1.13.0,<5.0', + ], + dev=[ + 'logging_tree>=1.7', + 'pygments>=2.2', + 'psutil>=5.0', + 'objgraph>=3.0', + ], + django=[ + 'django>=1.10,<2.0', + ], + flask=[ + 'flask>=0.11,<2.0', + 'blinker>=1.4,<2.0', + ], + pg=[ + 'sqlparse', + 'psycopg2', + ], + prometheus=[ + 'prometheus-client>=0.2.0,<0.5.0' + ',!=0.4.0,!=0.4.1', + ], + ), + include_package_data=True, + install_requires=[ + 'gunicorn>=19.7.0,<20.0', + 'Werkzeug>=0.11.5,<0.15', 'statsd>=3.2.1,<4.0', + 'requests>=2.10.0,<3.0', # renovate: ignore + 'raven>=2.11.0', # pyup: nothing + 'future>=0.15.2,<0.17', + 'ipaddress>=1.0.16,<2.0;python_version<\\"3.3\\"', + ], + keywords=[ + 'talisker', + ], + name='talisker', + package_data=dict( + talisker=[ + 'logstash/*', + ], + ), + package_dir=dict( + talisker='talisker', + ), + packages=[ + 'talisker', + ], + test_suite='tests', + url='https://github.com/canonical-ols/talisker', + version='0.9.16', + zip_safe=False, +) +" +`; + +exports[`manager/pip_requirements/update updateDependency handles multiple dependencies in same lines in setup.py 1`] = ` +" +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +setup( + author='Simon Davy', + author_email='simon.davy@canonical.com', + classifiers=[ + 'License :: OSI Approved :: Apache Software License', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Topic :: Internet :: WWW/HTTP :: WSGI', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', + 'Topic :: System :: Logging', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + ], + description='A common WSGI stack', + entry_points=dict( + console_scripts=[ + 'talisker=talisker:run_gunicorn', + 'talisker.run=talisker:run', + 'talisker.gunicorn=talisker:run_gunicorn', + 'talisker.gunicorn.eventlet=talisker:run_gunicorn_eventlet', + 'talisker.gunicorn.gevent=talisker:run_gunicorn_gevent', + 'talisker.celery=talisker:run_celery', + ], + ), + extras_require=dict( + celery=[ + 'celery>=3.1.13.0,<5.0', + ], + dev=[ + 'logging_tree>=1.7', + 'pygments>=2.2', + 'psutil>=5.0', + 'objgraph>=3.0', + ], + django=[ + 'django>=1.10,<2.0', + ], + flask=[ + 'flask>=0.11,<2.0', + 'blinker>=1.4,<2.0', + ], + pg=[ + 'sqlparse', + 'psycopg2', + ], + prometheus=[ + 'prometheus-client>=0.2.0,<0.5.0' + ',!=0.4.0,!=0.4.1', + ], + ), + include_package_data=True, + install_requires=['gunicorn>=19.7.0,<20.0', 'Werkzeug>=0.11.5,<0.15', 'pycryptodome==3.8.0','statsd>=3.2.1,<4.0', 'requests>=2.10.0,<3.0', 'raven>=5.27.1,<7.0','future>=0.15.2,<0.17',], + keywords=[ + 'talisker', + ], + name='talisker', + package_data=dict( + talisker=[ + 'logstash/*', + ], + ), + package_dir=dict( + talisker='talisker', + ), + packages=[ + 'talisker', + ], + test_suite='tests', + url='https://github.com/canonical-ols/talisker', + version='0.9.16', + zip_safe=False, +) +" +`; + exports[`manager/pip_requirements/update updateDependency replaces existing value 1`] = ` "--index-url http://example.com/private-pypi/ # simple comment diff --git a/test/manager/pip_requirements/update.spec.js b/test/manager/pip_requirements/update.spec.js index d25da17244..8a25d15ed5 100644 --- a/test/manager/pip_requirements/update.spec.js +++ b/test/manager/pip_requirements/update.spec.js @@ -18,6 +18,16 @@ const requirements4 = fs.readFileSync( 'utf8' ); +const setupPy1 = fs.readFileSync( + 'test/manager/pip_setup/_fixtures/setup.py', + 'utf-8' +); + +const setupPy2 = fs.readFileSync( + 'test/manager/pip_setup/_fixtures/setup-2.py', + 'utf-8' +); + describe('manager/pip_requirements/update', () => { describe('updateDependency', () => { it('replaces existing value', () => { @@ -58,5 +68,30 @@ describe('manager/pip_requirements/update', () => { expect(res).not.toEqual(requirements4); expect(res.includes(upgrade.newValue)).toBe(true); }); + it('handles dependencies in different lines in setup.py', () => { + const upgrade = { + depName: 'requests', + lineNumber: 64, + newValue: '>=2.11.0', + }; + const res = updateDependency(setupPy1, upgrade); + expect(res).toMatchSnapshot(); + expect(res).not.toEqual(setupPy1); + expect(res.includes(upgrade.newValue)).toBe(true); + }); + it('handles multiple dependencies in same lines in setup.py', () => { + const upgrade = { + depName: 'pycryptodome', + lineNumber: 60, + newValue: '==3.8.0', + }; + const res = updateDependency(setupPy2, upgrade); + expect(res).toMatchSnapshot(); + expect(res).not.toEqual(setupPy2); + const expectedUpdate = + "install_requires=['gunicorn>=19.7.0,<20.0', 'Werkzeug>=0.11.5,<0.15', 'pycryptodome==3.8.0','statsd>=3.2.1,<4.0', 'requests>=2.10.0,<3.0', 'raven>=5.27.1,<7.0','future>=0.15.2,<0.17',],"; + expect(res).toContain(expectedUpdate); + expect(res.includes(upgrade.newValue)).toBe(true); + }); }); }); diff --git a/test/manager/pip_setup/_fixtures/setup-2.py b/test/manager/pip_setup/_fixtures/setup-2.py new file mode 100644 index 0000000000..de35905d41 --- /dev/null +++ b/test/manager/pip_setup/_fixtures/setup-2.py @@ -0,0 +1,81 @@ + +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + +setup( + author='Simon Davy', + author_email='simon.davy@canonical.com', + classifiers=[ + 'License :: OSI Approved :: Apache Software License', + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Topic :: Internet :: WWW/HTTP :: WSGI', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', + 'Topic :: System :: Logging', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + ], + description='A common WSGI stack', + entry_points=dict( + console_scripts=[ + 'talisker=talisker:run_gunicorn', + 'talisker.run=talisker:run', + 'talisker.gunicorn=talisker:run_gunicorn', + 'talisker.gunicorn.eventlet=talisker:run_gunicorn_eventlet', + 'talisker.gunicorn.gevent=talisker:run_gunicorn_gevent', + 'talisker.celery=talisker:run_celery', + ], + ), + extras_require=dict( + celery=[ + 'celery>=3.1.13.0,<5.0', + ], + dev=[ + 'logging_tree>=1.7', + 'pygments>=2.2', + 'psutil>=5.0', + 'objgraph>=3.0', + ], + django=[ + 'django>=1.10,<2.0', + ], + flask=[ + 'flask>=0.11,<2.0', + 'blinker>=1.4,<2.0', + ], + pg=[ + 'sqlparse', + 'psycopg2', + ], + prometheus=[ + 'prometheus-client>=0.2.0,<0.5.0' + ',!=0.4.0,!=0.4.1', + ], + ), + include_package_data=True, + install_requires=['gunicorn>=19.7.0,<20.0', 'Werkzeug>=0.11.5,<0.15', 'pycryptodome==3.7.3','statsd>=3.2.1,<4.0', 'requests>=2.10.0,<3.0', 'raven>=5.27.1,<7.0','future>=0.15.2,<0.17',], + keywords=[ + 'talisker', + ], + name='talisker', + package_data=dict( + talisker=[ + 'logstash/*', + ], + ), + package_dir=dict( + talisker='talisker', + ), + packages=[ + 'talisker', + ], + test_suite='tests', + url='https://github.com/canonical-ols/talisker', + version='0.9.16', + zip_safe=False, +) -- GitLab