Commit ca6d5096 authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki Committed by Xavier Thompson

[feat] zc.recipe.egg: Improve on the fly patches.

Now specified patches are automatically applied on required eggs as well.
parent de36e733
...@@ -763,6 +763,9 @@ class Installer(object): ...@@ -763,6 +763,9 @@ class Installer(object):
else: else:
logger.debug('Adding required %r', str(req)) logger.debug('Adding required %r', str(req))
self._log_requirement(ws, req) self._log_requirement(ws, req)
if patch_dict and req.project_name in patch_dict:
self._env.scan(
self.build(str(req), {}, patch_dict=patch_dict))
for dist in self._get_dist(req, ws): for dist in self._get_dist(req, ws):
self._maybe_add_setuptools(ws, dist) self._maybe_add_setuptools(ws, dist)
if dist not in req: if dist not in req:
......
...@@ -57,17 +57,21 @@ class Eggs(object): ...@@ -57,17 +57,21 @@ class Eggs(object):
options['develop-eggs-directory'] = b_options['develop-eggs-directory'] options['develop-eggs-directory'] = b_options['develop-eggs-directory']
options['_d'] = options['develop-eggs-directory'] # backward compat. options['_d'] = options['develop-eggs-directory'] # backward compat.
def _get_patch_dict(self, options, distribution_list): def _get_patch_dict(self, options, egg=None):
patch_dict = {} patch_dict = {}
global_patch_binary = options.get('patch-binary', 'patch') global_patch_binary = options.get('patch-binary', 'patch')
if egg:
egg = re.sub('[<>=].*', '', egg)
egg_list = [egg]
else:
egg_list = [x[:-8] for x in options.keys() if x.endswith('-patches')]
def get_option(egg, key, default): def get_option(egg, key, default):
if len(distribution_list) == 1: if len(egg_list) == 1:
return options.get('%s-%s' % (egg, key), return options.get('%s-%s' % (egg, key),
options.get(key, default)) options.get(key, default))
else: else:
return options.get('%s-%s' % (egg, key), default) return options.get('%s-%s' % (egg, key), default)
for distribution in distribution_list: for egg in egg_list:
egg = re.sub('[<>=].*', '', distribution)
patches = filter(lambda x:x, patches = filter(lambda x:x,
map(lambda x:x.strip(), map(lambda x:x.strip(),
get_option(egg, 'patches', '').splitlines())) get_option(egg, 'patches', '').splitlines()))
...@@ -162,7 +166,7 @@ class Eggs(object): ...@@ -162,7 +166,7 @@ class Eggs(object):
[develop_eggs_dir, eggs_dir] [develop_eggs_dir, eggs_dir]
) )
else: else:
patch_dict = self._get_patch_dict(self.options, distributions) patch_dict = self._get_patch_dict(self.options)
ws = zc.buildout.easy_install.install( ws = zc.buildout.easy_install.install(
distributions, eggs_dir, distributions, eggs_dir,
links=links, links=links,
......
Patching eggs before installation
---------------------------------
The SlapOS extensions of ``zc.recipe.egg`` supports applying patches before installing eggs.
The syntax is to use a version with the magic string ``SlapOSPatched`` plus the number of
patches to apply.
Let's use a patch for demoneeded egg:
>>> write(sample_buildout, 'demoneeded.patch',
... r"""diff -ru before/demoneeded-1.1/eggrecipedemoneeded.py after/demoneeded-1.1/eggrecipedemoneeded.py
... --- before/demoneeded-1.1/eggrecipedemoneeded.py 2020-09-08 09:27:36.000000000 +0200
... +++ after/demoneeded-1.1/eggrecipedemoneeded.py 2020-09-08 09:46:16.482243822 +0200
... @@ -1,3 +1,3 @@
... -y=1
... +y="patched demoneeded"
... def f():
... pass
... \ No newline at end of file
... """)
First, we install demoneeded directly:
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demoneeded
...
... [demoneeded]
... recipe = zc.recipe.egg:eggs
... eggs = demoneeded
... find-links = %(server)s
... index = %(server)s/index
... demoneeded-patches =
... ./demoneeded.patch#4b8ad56711dd0d898a2b7957e9604079
... demoneeded-patch-options = -p2
...
... [versions]
... demoneeded = 1.1+SlapOSPatched001
... """ % dict(server=link_server))
When running buildout, we have a warning that a different version is installed, but that's not fatal.
>>> print_(system(buildout), end='')
Installing demoneeded.
patching file eggrecipedemoneeded.py
Installing demoneeded 1.1
Caused installation of a distribution:
demoneeded 1.1+slapospatched001
with a different version.
The installed egg has the slapospatched001 marker
>>> ls(sample_buildout, 'eggs')
d demoneeded-1.1+slapospatched001-pyN.N.egg
...pip...
...setuptools...
...wheel...
zc.buildout.egg
The code of the egg has been patched:
>>> import glob
>>> import os.path
>>> cat(glob.glob(os.path.join(sample_buildout, 'eggs', 'demoneeded-1.1+slapospatched001*', 'eggrecipedemoneeded.py'))[0])
y="patched demoneeded"
def f():
pass
Reset the state and also remove the installed egg
>>> remove('.installed.cfg')
>>> rmdir(glob.glob(os.path.join(sample_buildout, 'eggs', 'demoneeded-1.1+slapospatched001*'))[0])
In the previous example we applied patches to an egg installed directly, but
the same technique can be used to apply patches on eggs installed as dependencies.
In this example we install demo and apply a patch to demoneeded, which is a dependency to demo.
>>> write(sample_buildout, 'buildout.cfg',
... """
... [buildout]
... parts = demo
...
... [demo]
... recipe = zc.recipe.egg
... eggs = demo
... find-links = %(server)s
... index = %(server)s/index
... demoneeded-patches =
... ./demoneeded.patch#4b8ad56711dd0d898a2b7957e9604079
... demoneeded-patch-options = -p2
...
... [versions]
... demoneeded = 1.1+SlapOSPatched001
... """ % dict(server=link_server))
When running buildout, we also have that warning that a different version is installed.
>>> print_(system(buildout), end='')
Installing demo.
Getting distribution for 'demo'.
Got demo 0.3.
patching file eggrecipedemoneeded.py
Installing demoneeded 1.1
Caused installation of a distribution:
demoneeded 1.1+slapospatched001
with a different version.
Generated script '/sample-buildout/bin/demo'.
The installed egg has the slapospatched001 marker
>>> ls(sample_buildout, 'eggs')
d demo-0.3-pyN.N.egg
d demoneeded-1.1+slapospatched001-pyN.N.egg
...pip...
...setuptools...
...wheel...
zc.buildout.egg
If we run the demo script we see the patch was applied:
>>> print_(system(join(sample_buildout, 'bin', 'demo')), end='')
3 patched demoneeded
...@@ -106,6 +106,28 @@ def test_suite(): ...@@ -106,6 +106,28 @@ def test_suite():
''), ''),
]) ])
), ),
doctest.DocFileSuite(
'patches.rst',
setUp=setUp, tearDown=zc.buildout.testing.buildoutTearDown,
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
checker=renormalizing.RENormalizing([
zc.buildout.testing.normalize_path,
zc.buildout.testing.normalize_endings,
zc.buildout.testing.normalize_script,
zc.buildout.testing.normalize_egg_py,
zc.buildout.tests.normalize_bang,
zc.buildout.tests.normalize_S,
zc.buildout.testing.not_found,
zc.buildout.testing.python27_warning,
zc.buildout.testing.python27_warning_2,
zc.buildout.testing.easyinstall_deprecated,
(re.compile(r'[d-] zc.buildout(-\S+)?[.]egg(-link)?'),
'zc.buildout.egg'),
(re.compile(r'[d-] setuptools-[^-]+-'), 'setuptools-X-'),
(re.compile(r'eggs\\\\demo'), 'eggs/demo'),
(re.compile(r'[a-zA-Z]:\\\\foo\\\\bar'), '/foo/bar'),
])
),
] ]
if not WINDOWS: if not WINDOWS:
suites.append( suites.append(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment