Merge lp:~gary/z3c.recipe.filetemplate/support-system-python into lp:~gary/z3c.recipe.filetemplate/trunk

Proposed by Gary Poster
Status: Needs review
Proposed branch: lp:~gary/z3c.recipe.filetemplate/support-system-python
Merge into: lp:~gary/z3c.recipe.filetemplate/trunk
Diff against target: None lines
To merge this branch: bzr merge lp:~gary/z3c.recipe.filetemplate/support-system-python
Reviewer Review Type Date Requested Status
Francis J. Lacoste (community) Approve
Review via email: mp+8616@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Gary Poster (gary) wrote :

Support the new zc.buildout 1.4.0+ include-site-packages option, and more.

--------
Features
--------

- Support the new zc.buildout 1.4.0+ include-site-packages option.

- Use the new zc.buildout 1.4.0+ path sorting algorithm, which puts
  site-package dependency paths after the other dependency paths (but before
  extra paths, as before). This can reduce or eliminate problems with
  packages in site-packages causing other dependencies to be masked with the
  versions in site-packages.

- Support escaping "$" with "$$" in templates. This is particularly useful
  for *NIX shell scripts.

- Support specifying local options in templates without braces (e.g.,
  "Hello $world" is now equivalent to "Hello ${world}".

-----
Fixes
-----

- Clarify that the recipe does not support the relative-paths options.

- Make tests less susceptible to timing errors.

Revision history for this message
Gary Poster (gary) wrote :

Sorry, this change was removed from the branch, but I mistakenly added it to the comment. (I removed it because, upon trying it in Launchpad, I decided that it caused annoyances for little gain).

NOT PART OF THIS BRANCH:

- Support specifying local options in templates without braces (e.g.,
  "Hello $world" is now equivalent to "Hello ${world}".

Revision history for this message
Francis J. Lacoste (flacoste) wrote :
Download full text (6.4 KiB)

On July 11, 2009, Gary Poster wrote:
> Gary Poster has proposed merging
> lp:~gary/z3c.recipe.filetemplate/support-system-python into
> lp:~gary/z3c.recipe.filetemplate/trunk.
>
> Requested reviews:
> Francis J. Lacoste (flacoste)
>
> Support the new zc.buildout 1.4.0+ include-site-packages option, and more.
>

Hi Gary,

This all looks good. My only concern is the new write_and_wait which I don't
understand why it's necessary.

> === modified file 'z3c/recipe/filetemplate/__init__.py'

> @@ -195,17 +202,19 @@
> 'Destinations already exist: %s. Please make sure that '
> 'you really want to generate these automatically. Then '
> 'move them away.', ', '.join(already_exists))
> + seen = [] # we throw this away right now, but could move template

Sentence should start with a capital.

> + # processing up to __init__ if valuable. That would mean that
templates
> + # would be rewritten even if a value in another section had been
> + # referenced; however, it would also mean that __init__ would do
> + # virtually all of the work, with install only doing the writing.
> for rel_path, last_mod, st_mode in self.actions:
> source = os.path.join(self.source_dir, rel_path)
> dest = os.path.join(self.destination_dir, rel_path[:-3])
> mode=stat.S_IMODE(st_mode)
> - template=open(source).read()
> - template=re.sub(r"\$\{([^:]+?)\}", r"${%s:\1}" % self.name,
> - template)
> - self._create_paths(os.path.dirname(dest))
> # we process the file first so that it won't be created if
there
> # is a problem.
> - processed = self.options._sub(template, [])
> + processed = Template(source).substitute(self, seen)
> + self._create_paths(os.path.dirname(dest))
> result=open(dest, "wt")
> result.write(processed)
> result.close()
> @@ -221,3 +230,74 @@
>
> def update(self):
> pass
> +
> +
> +class Template:
> + # hacked from string.Template
> + pattern = re.compile(r"""
> + \$(?:
> + \${(?P<escaped>[^}]*)} | # Escape sequence of two
delimiters.
> + {(?P<braced_single>[-a-z0-9 ._]+)} |
> + # Delimiter and a braced local
option
> + {(?P<braced_double>[-a-z0-9 ._]+:[-a-z0-9 ._]+)} |
> + # Delimiter and a braced fully
> + # qualified option (that is, with
> + # explicit section).
> + {(?P<invalid>[^}]*}) # Other ill-formed delimiter
exprs.
> + )
> + """, re.IGNORECASE | re.VERBOSE)
> +
> + def __init__(self, source):
> + self.source = source
> + self.template = open(source).read()
> +
> + def _get_colno_lineno(self, i):
> + lines = self.template[:i].splitlines(True)
> + if not lines:
> + colno = 1
> + lineno = 1
> + else:
> + colno = i - len(''.join(lines[:-1]))
> + ...

Read more...

Revision history for this message
Gary Poster (gary) wrote :
Download full text (9.3 KiB)

On Jul 13, 2009, at 5:21 PM, Francis J. Lacoste wrote:

> On July 11, 2009, Gary Poster wrote:
>> Gary Poster has proposed merging
>> lp:~gary/z3c.recipe.filetemplate/support-system-python into
>> lp:~gary/z3c.recipe.filetemplate/trunk.
>>
>> Requested reviews:
>> Francis J. Lacoste (flacoste)
>>
>> Support the new zc.buildout 1.4.0+ include-site-packages option,
>> and more.
>>
>
> Hi Gary,
>
> This all looks good. My only concern is the new write_and_wait which
> I don't
> understand why it's necessary.

Cool. I'll discuss below.

>
>> === modified file 'z3c/recipe/filetemplate/__init__.py'
>
>> @@ -195,17 +202,19 @@
>> 'Destinations already exist: %s. Please make sure
>> that '
>> 'you really want to generate these automatically.
>> Then '
>> 'move them away.', ', '.join(already_exists))
>> + seen = [] # we throw this away right now, but could move
>> template
>
> Sentence should start with a capital.

Ack. See incremental diff.

>
>> + # processing up to __init__ if valuable. That would mean
>> that
> templates
>> + # would be rewritten even if a value in another section
>> had been
>> + # referenced; however, it would also mean that __init__
>> would do
>> + # virtually all of the work, with install only doing the
>> writing.
>> for rel_path, last_mod, st_mode in self.actions:
>> source = os.path.join(self.source_dir, rel_path)
>> dest = os.path.join(self.destination_dir, rel_path[:-3])
>> mode=stat.S_IMODE(st_mode)
>> - template=open(source).read()
>> - template=re.sub(r"\$\{([^:]+?)\}", r"${%s:\1}" %
>> self.name,
>> - template)
>> - self._create_paths(os.path.dirname(dest))
>> # we process the file first so that it won't be created
>> if
> there
>> # is a problem.
>> - processed = self.options._sub(template, [])
>> + processed = Template(source).substitute(self, seen)
>> + self._create_paths(os.path.dirname(dest))
>> result=open(dest, "wt")
>> result.write(processed)
>> result.close()
>> @@ -221,3 +230,74 @@
>>
>> def update(self):
>> pass
>> +
>> +
>> +class Template:
>> + # hacked from string.Template
>> + pattern = re.compile(r"""
>> + \$(?:
>> + \${(?P<escaped>[^}]*)} | # Escape sequence
>> of two
> delimiters.
>> + {(?P<braced_single>[-a-z0-9 ._]+)} |
>> + # Delimiter and a braced
>> local
> option
>> + {(?P<braced_double>[-a-z0-9 ._]+:[-a-z0-9 ._]+)} |
>> + # Delimiter and a braced
>> fully
>> + # qualified option (that
>> is, with
>> + # explicit section).
>> + {(?P<invalid>[^}]*}) # Other ill-formed
>> delimiter
> exprs.
>> + )
>> + """, re.IGNORECASE | re.VERBOSE)
>> +
>> + def __init__(self, source):
>> + self.source = source
>> + self.template = open(source)....

Read more...

21. By Gary Poster

changes from flacosate review, merged from svn

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

On July 13, 2009, Gary Poster wrote:
> In general, the necessity is caused by the mtime precision. On my
> Mac, the precision is 1 second. Here's an example run of a slightly
> modified version of the helper (adding a print), in the interactive
> prompt.
>
> >>> def write_and_wait(dir, *args):
> ... path = os.path.join(dir, *(args[:-1]))
> ... original = os.stat(path).st_mtime
> ... while os.stat(path).st_mtime == original:
> ... print original
> ... f = open(path, 'w')
> ... f.write(args[-1])
> ... f.flush()
> ... os.fsync(f.fileno())
> ... f.close()
> ...
> >>> f = open('/Users/gary/dev/deleteme', 'w')
> >>> f.write('test')
> >>> f.close()
> >>> write_and_wait('/Users/gary/dev/deleteme', 'test')
> 1247536625.0
> >>> write_and_wait('/Users/gary/dev/deleteme', 'test')
> 1247536627.0
> >>> write_and_wait('/Users/gary/dev/deleteme', 'test')
> 1247536628.0
> >>> write_and_wait('/Users/gary/dev/deleteme', 'test')
> 1247536629.0
> 1247536629.0
> 1247536629.0
> [...there are 299 of these...]
> >>>
>
> So, the precision is the point. I tried a variety of things to
> trigger the mtime, but writing an empty string didn't work, and
> directly writing the mtime is OS-specific/dependent, and I didn't
> really want to go there. This is a test harness. Good enough, was my
> thinking.
>
> As far as all the flushing and so on, that's what Jim had for his
> ``write`` helper function, so I suspect there's a point there too. It
> could probably be somewhat refactored but...it's a test harness.

Simply add a docstring explaining that we use this helper to update files to
make sure that the mtime of the file is updated so that buildout pick up the
changes, with a note that the resolution of mtime is system dependant. (And
possibly rename it to update_file() or something like that?)

And instead of busy-waiting, why don't you add a time.sleep(0.5) in there?

Cheers

  review approve

--
Francis J. Lacoste
<email address hidden>

review: Approve
Revision history for this message
Gary Poster (gary) wrote :
Download full text (4.3 KiB)

On Jul 14, 2009, at 9:27 AM, Francis J. Lacoste wrote:

> Review: Approve
> On July 13, 2009, Gary Poster wrote:
>> In general, the necessity is caused by the mtime precision. On my
>> Mac, the precision is 1 second. Here's an example run of a slightly
>> modified version of the helper (adding a print), in the interactive
>> prompt.
>>
>>>>> def write_and_wait(dir, *args):
>> ... path = os.path.join(dir, *(args[:-1]))
>> ... original = os.stat(path).st_mtime
>> ... while os.stat(path).st_mtime == original:
>> ... print original
>> ... f = open(path, 'w')
>> ... f.write(args[-1])
>> ... f.flush()
>> ... os.fsync(f.fileno())
>> ... f.close()
>> ...
>>>>> f = open('/Users/gary/dev/deleteme', 'w')
>>>>> f.write('test')
>>>>> f.close()
>>>>> write_and_wait('/Users/gary/dev/deleteme', 'test')
>> 1247536625.0
>>>>> write_and_wait('/Users/gary/dev/deleteme', 'test')
>> 1247536627.0
>>>>> write_and_wait('/Users/gary/dev/deleteme', 'test')
>> 1247536628.0
>>>>> write_and_wait('/Users/gary/dev/deleteme', 'test')
>> 1247536629.0
>> 1247536629.0
>> 1247536629.0
>> [...there are 299 of these...]
>>>>>
>>
>> So, the precision is the point. I tried a variety of things to
>> trigger the mtime, but writing an empty string didn't work, and
>> directly writing the mtime is OS-specific/dependent, and I didn't
>> really want to go there. This is a test harness. Good enough, was
>> my
>> thinking.
>>
>> As far as all the flushing and so on, that's what Jim had for his
>> ``write`` helper function, so I suspect there's a point there too.
>> It
>> could probably be somewhat refactored but...it's a test harness.
>
> Simply add a docstring explaining that we use this helper to update
> files to
> make sure that the mtime of the file is updated so that buildout
> pick up the
> changes, with a note that the resolution of mtime is system
> dependant. (And
> possibly rename it to update_file() or something like that?)
>
> And instead of busy-waiting, why don't you add a time.sleep(0.5) in
> there?

OK, cool, I like the name change in particular.

I actually left out the time.sleep as a policy-ish decision, wanting
the tests to go as fast as possible, but it was an experiment, and I'm
happy to switch back to the time.sleep approach.

Incremental diff below.

Thanks

Gary

Index: z3c/recipe/filetemplate/tests.py
===================================================================
--- z3c/recipe/filetemplate/tests.py (revision 101900)
+++ z3c/recipe/filetemplate/tests.py (revision 101901)
@@ -13,14 +13,21 @@

##############################################################################

  import os
+import time
  import zc.buildout.testing
  import zc.buildout.tests
  from zope.testing import doctest

-def write_and_wait(dir, *args):
+def update_file(dir, *args):
+ """Update a file.
+
+ Make sure that the mtime of the file is updated so that buildout
notices
+ the changes. The resolution of mtime is system dependent, so we
keep
+ trying to write until mtime has actually changed."""
      path = os.path.join(dir, *(args[:-1]))
      original = os.stat(path).st_mtime
      wh...

Read more...

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

On July 14, 2009, Gary Poster wrote:
> I actually left out the time.sleep as a policy-ish decision, wanting
> the tests to go as fast as possible, but it was an experiment, and I'm
> happy to switch back to the time.sleep approach.
>

Right, but know that busy waiting is bad in general because it makes it harder
for the OS to schedule other task (since you are competing for the CPU).

>
> Index: z3c/recipe/filetemplate/tests.py
> ===================================================================
> --- z3c/recipe/filetemplate/tests.py (revision 101900)
> +++ z3c/recipe/filetemplate/tests.py (revision 101901)
> @@ -13,14 +13,21 @@
>
> ###########################################################################
>###
>
> import os
> +import time
> import zc.buildout.testing
> import zc.buildout.tests
> from zope.testing import doctest
>
> -def write_and_wait(dir, *args):
> +def update_file(dir, *args):
> + """Update a file.
> +
> + Make sure that the mtime of the file is updated so that buildout
> notices
> + the changes. The resolution of mtime is system dependent, so we
> keep
> + trying to write until mtime has actually changed."""
> path = os.path.join(dir, *(args[:-1]))
> original = os.stat(path).st_mtime
> while os.stat(path).st_mtime == original:
> + time.sleep(0.2)
> f = open(path, 'w')
> f.write(args[-1])
> f.flush()

If you want the test to run as fast as possible I suggest:

while True:
  # Write the change
  # ...
  if os.stat(path).st_mtime != original:
   break
  time.sleep(0.2)

Use it if you like it.

--
Francis J. Lacoste
<email address hidden>

Revision history for this message
Gary Poster (gary) wrote :

On Jul 14, 2009, at 10:09 AM, Francis J. Lacoste wrote:

> On July 14, 2009, Gary Poster wrote:
>> I actually left out the time.sleep as a policy-ish decision, wanting
>> the tests to go as fast as possible, but it was an experiment, and
>> I'm
>> happy to switch back to the time.sleep approach.
>>
>
> Right, but know that busy waiting is bad in general because it makes
> it harder
> for the OS to schedule other task (since you are competing for the
> CPU).

Bah, get more cores. :-)

>> Index: z3c/recipe/filetemplate/tests.py
>> ===================================================================
>> --- z3c/recipe/filetemplate/tests.py (revision 101900)
>> +++ z3c/recipe/filetemplate/tests.py (revision 101901)
>> @@ -13,14 +13,21 @@
>>
>> ###########################################################################
>> ###
>>
>> import os
>> +import time
>> import zc.buildout.testing
>> import zc.buildout.tests
>> from zope.testing import doctest
>>
>> -def write_and_wait(dir, *args):
>> +def update_file(dir, *args):
>> + """Update a file.
>> +
>> + Make sure that the mtime of the file is updated so that buildout
>> notices
>> + the changes. The resolution of mtime is system dependent, so we
>> keep
>> + trying to write until mtime has actually changed."""
>> path = os.path.join(dir, *(args[:-1]))
>> original = os.stat(path).st_mtime
>> while os.stat(path).st_mtime == original:
>> + time.sleep(0.2)
>> f = open(path, 'w')
>> f.write(args[-1])
>> f.flush()
>
>
> If you want the test to run as fast as possible I suggest:
>
> while True:
> # Write the change
> # ...
> if os.stat(path).st_mtime != original:
> break
> time.sleep(0.2)
>
> Use it if you like it.

Sure, cool. Done.

Thanks

Gary

Unmerged revisions

21. By Gary Poster

changes from flacosate review, merged from svn

20. By gary

revert some of the features; they were annoying to me in practice. Also make error messages better.

19. By gary

Support the new zc.buildout 1.4.0+ include-site-packages option, and more.

--------
Features
--------

- Support the new zc.buildout 1.4.0+ include-site-packages option.

- Use the new zc.buildout 1.4.0+ path sorting algorithm, which puts
  site-package dependency paths after the other dependency paths (but before
  extra paths, as before). This can reduce or eliminate problems with
  packages in site-packages causing other dependencies to be masked with the
  versions in site-packages.

- Support escaping "$" with "$$" in templates. This is particularly useful
  for *NIX shell scripts.

- Support specifying local options in templates without braces (e.g.,
  "Hello $world" is now equivalent to "Hello ${world}".

-----
Fixes
-----

- Clarify that the recipe does not support the relative-paths options.

- Make tests less susceptible to timing errors.

18. By gary

make a branch for supporting zc.buildout 1.4.0 (hopefully) that also has some additional features

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CHANGES.txt'
--- CHANGES.txt 2009-07-02 18:29:12 +0000
+++ CHANGES.txt 2009-07-10 02:59:42 +0000
@@ -9,7 +9,24 @@
9Features9Features
10--------10--------
1111
12- None yet.12- Support the new zc.buildout 1.4.0+ include-site-packages option.
13
14- Use the new zc.buildout 1.4.0+ path sorting algorithm, which puts
15 site-package dependency paths after the other dependency paths (but before
16 extra paths, as before). This can reduce or eliminate problems with
17 packages in site-packages causing other dependencies to be masked with the
18 versions in site-packages.
19
20- Support escaping "${...}" with "$${...}" in templates. This is particularly
21 useful for *NIX shell scripts.
22
23-----
24Fixes
25-----
26
27- Clarify that the recipe does not support the relative-paths options.
28
29- Make tests less susceptible to timing errors.
1330
1431
152.0.3 (2009-07-02)322.0.3 (2009-07-02)
1633
=== modified file 'buildout.cfg'
--- buildout.cfg 2007-09-30 19:14:00 +0000
+++ buildout.cfg 2009-07-09 18:20:56 +0000
@@ -1,7 +1,17 @@
1[buildout]1[buildout]
2develop = .2develop = .
3 zc.buildout
4 zc.buildout/zc.recipe.egg_
3parts = test5parts = test
6 interpreter
7include-site-packages = false
48
5[test]9[test]
6recipe = zc.recipe.testrunner10recipe = zc.recipe.testrunner
7eggs = z3c.recipe.filetemplate11eggs = z3c.recipe.filetemplate
12
13[interpreter]
14recipe = zc.recipe.egg
15interpreter = py
16eggs = z3c.recipe.filetemplate
17
818
=== modified file 'setup.py'
--- setup.py 2009-05-04 17:31:38 +0000
+++ setup.py 2009-07-09 18:20:56 +0000
@@ -42,8 +42,8 @@
42 packages=find_packages(),42 packages=find_packages(),
43 namespace_packages=['z3c', 'z3c.recipe'],43 namespace_packages=['z3c', 'z3c.recipe'],
44 install_requires=['setuptools',44 install_requires=['setuptools',
45 'zc.buildout',45 'zc.buildout>=1.4.0dev',
46 'zc.recipe.egg',46 'zc.recipe.egg>=1.3.0dev',
47 ],47 ],
48 zip_safe=True,48 zip_safe=True,
49 entry_points="""49 entry_points="""
5050
=== modified file 'z3c/recipe/filetemplate/README.txt'
--- z3c/recipe/filetemplate/README.txt 2009-04-30 21:38:40 +0000
+++ z3c/recipe/filetemplate/README.txt 2009-07-10 02:59:42 +0000
@@ -40,7 +40,7 @@
40 ... world = Philipp40 ... world = Philipp
41 ... """)41 ... """)
4242
43After executing buildout, we can see that ``$world`` has indeed been43After executing buildout, we can see that ``${world}`` has indeed been
44replaced by ``Philipp``:44replaced by ``Philipp``:
4545
46 >>> print system(buildout)46 >>> print system(buildout)
@@ -49,6 +49,35 @@
49 >>> cat(sample_buildout, 'helloworld.txt')49 >>> cat(sample_buildout, 'helloworld.txt')
50 Hello Philipp!50 Hello Philipp!
5151
52If you need to escape the ${...} pattern, you can do so by repeating the dollar
53sign.
54
55 >>> write_and_wait(sample_buildout, 'helloworld.txt.in',
56 ... """
57 ... Hello world! The double $${dollar-sign} escapes!
58 ... """)
59
60 >>> print system(buildout)
61 Uninstalling message.
62 Installing message.
63
64 >>> cat(sample_buildout, 'helloworld.txt')
65 Hello world! The double ${dollar-sign} escapes!
66
67Note that dollar signs alone, without curly braces, are not parsed.
68
69 >>> write_and_wait(sample_buildout, 'helloworld.txt.in',
70 ... """
71 ... $Hello $$world! $$$profit!
72 ... """)
73
74 >>> print system(buildout)
75 Uninstalling message.
76 Installing message.
77
78 >>> cat(sample_buildout, 'helloworld.txt')
79 $Hello $$world! $$$profit!
80
52Note that the output file uses the same permission bits as found on the input81Note that the output file uses the same permission bits as found on the input
53file.82file.
5483
@@ -327,13 +356,20 @@
327 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS356 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
328 Hello! Here are the paths for the demo<0.3 eggs.357 Hello! Here are the paths for the demo<0.3 eggs.
329 OS paths:358 OS paths:
330 .../eggs/demo-0.2...egg:.../eggs/demoneeded-1.2c1...egg359 .../eggs/demo-0.2...egg:.../eggs/demoneeded-1.2c1...egg:...
331 ---360 ---
332 String paths:361 String paths:
333 '.../eggs/demo-0.2...egg', '.../eggs/demoneeded-1.2c1...egg'362 '.../eggs/demo-0.2...egg', '.../eggs/demoneeded-1.2c1...egg', '...'
334 ---363 ---
335 Space paths:364 Space paths:
336 .../eggs/demo-0.2...egg .../eggs/demoneeded-1.2c1...egg365 .../eggs/demo-0.2...egg .../eggs/demoneeded-1.2c1...egg ...
366
367Notice that included multiple paths. In fact, it includes the site packages
368and the standard library, so these are appropriate for entirely replacing
369sys.path.
370
371You can eliminate the site packages from the paths by specifying
372"include-site-packages = false" in the buildout or the specific section.
337373
338You can specify extra-paths as well, which will go at the end of the egg paths.374You can specify extra-paths as well, which will go at the end of the egg paths.
339375
@@ -359,13 +395,13 @@
359 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS395 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
360 Hello! Here are the paths for the demo<0.3 eggs.396 Hello! Here are the paths for the demo<0.3 eggs.
361 OS paths:397 OS paths:
362 ...demo...:...demoneeded...:.../sample-buildout/foo398 ...demo...:...demoneeded...:.../sample-buildout/foo:...
363 ---399 ---
364 String paths:400 String paths:
365 '...demo...', '...demoneeded...', '.../sample-buildout/foo'401 '...demo...', '...demoneeded...', '.../sample-buildout/foo', '...'
366 ---402 ---
367 Space paths:403 Space paths:
368 ...demo... ...demoneeded... .../sample-buildout/foo404 ...demo... ...demoneeded... .../sample-buildout/foo ...
369405
370Defining options in Python406Defining options in Python
371==========================407==========================
@@ -420,8 +456,8 @@
420456
421 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS457 >>> cat(sample_buildout, 'helloworld.txt') # doctest:+ELLIPSIS
422 hello world!458 hello world!
423 duplicate-os-paths: ...demo-0.2...egg:...demoneeded-1.2c1...egg459 duplicate-os-paths: ...demo-0.2...egg:...demoneeded-1.2c1...egg:...
424 foo-paths: ...demo-0.2...eggFOO...demoneeded-1.2c1...egg460 foo-paths: ...demo-0.2...eggFOO...demoneeded-1.2c1...eggFOO...
425 silly-range: [0, 1, 2, 3, 4]461 silly-range: [0, 1, 2, 3, 4]
426 first-interpreted-option: duplicate-os-paths=(os.pathsep).join(paths)462 first-interpreted-option: duplicate-os-paths=(os.pathsep).join(paths)
427 message-reversed-is-egassem: egassem463 message-reversed-is-egassem: egassem
428464
=== modified file 'z3c/recipe/filetemplate/__init__.py'
--- z3c/recipe/filetemplate/__init__.py 2009-06-06 12:31:22 +0000
+++ z3c/recipe/filetemplate/__init__.py 2009-07-10 02:59:42 +0000
@@ -22,6 +22,7 @@
22import traceback22import traceback
23import zc.recipe.egg23import zc.recipe.egg
24import zc.buildout24import zc.buildout
25import zc.buildout.buildout
25import zc.buildout.easy_install26import zc.buildout.easy_install
2627
27ABS_PATH_ERROR = ('%s is an absolute path. Paths must be '28ABS_PATH_ERROR = ('%s is an absolute path. Paths must be '
@@ -44,15 +45,21 @@
44 self.options.setdefault(key, value)45 self.options.setdefault(key, value)
45 # set up paths for eggs, if given46 # set up paths for eggs, if given
46 if 'eggs' in self.options:47 if 'eggs' in self.options:
48 relative_paths = self.options.get(
49 'relative-paths',
50 buildout['buildout'].get('relative-paths', 'false')
51 )
52 if relative_paths != 'false':
53 self._user_error(
54 'This recipe does not support relative-paths.')
55 # Why? Because the relative path tricks rely on Python
56 # at runtime, and we're offering path values for arbitrary
57 # files (for instance, including bash files).
47 self.eggs = zc.recipe.egg.Scripts(buildout, name, options)58 self.eggs = zc.recipe.egg.Scripts(buildout, name, options)
48 orig_distributions, ws = self.eggs.working_set()59 orig_distributions, ws = self.eggs.working_set()
49 # we want ws, eggs.extra_paths, eggs._relative_paths60 all_paths = zc.buildout.easy_install.get_path(
50 all_paths = [61 ws, self.options['executable'], self.eggs.extra_paths,
51 zc.buildout.easy_install.realpath(dist.location)62 self.eggs.include_site_packages)
52 for dist in ws]
53 all_paths.extend(
54 zc.buildout.easy_install.realpath(path)
55 for path in self.eggs.extra_paths)
56 else:63 else:
57 all_paths = []64 all_paths = []
58 paths = [path for path in all_paths if not path.endswith('.zip')]65 paths = [path for path in all_paths if not path.endswith('.zip')]
@@ -195,17 +202,19 @@
195 'Destinations already exist: %s. Please make sure that '202 'Destinations already exist: %s. Please make sure that '
196 'you really want to generate these automatically. Then '203 'you really want to generate these automatically. Then '
197 'move them away.', ', '.join(already_exists))204 'move them away.', ', '.join(already_exists))
205 seen = [] # we throw this away right now, but could move template
206 # processing up to __init__ if valuable. That would mean that templates
207 # would be rewritten even if a value in another section had been
208 # referenced; however, it would also mean that __init__ would do
209 # virtually all of the work, with install only doing the writing.
198 for rel_path, last_mod, st_mode in self.actions:210 for rel_path, last_mod, st_mode in self.actions:
199 source = os.path.join(self.source_dir, rel_path)211 source = os.path.join(self.source_dir, rel_path)
200 dest = os.path.join(self.destination_dir, rel_path[:-3])212 dest = os.path.join(self.destination_dir, rel_path[:-3])
201 mode=stat.S_IMODE(st_mode)213 mode=stat.S_IMODE(st_mode)
202 template=open(source).read()
203 template=re.sub(r"\$\{([^:]+?)\}", r"${%s:\1}" % self.name,
204 template)
205 self._create_paths(os.path.dirname(dest))
206 # we process the file first so that it won't be created if there214 # we process the file first so that it won't be created if there
207 # is a problem.215 # is a problem.
208 processed = self.options._sub(template, [])216 processed = Template(source).substitute(self, seen)
217 self._create_paths(os.path.dirname(dest))
209 result=open(dest, "wt")218 result=open(dest, "wt")
210 result.write(processed)219 result.write(processed)
211 result.close()220 result.close()
@@ -221,3 +230,74 @@
221230
222 def update(self):231 def update(self):
223 pass232 pass
233
234
235class Template:
236 # hacked from string.Template
237 pattern = re.compile(r"""
238 \$(?:
239 \${(?P<escaped>[^}]*)} | # Escape sequence of two delimiters.
240 {(?P<braced_single>[-a-z0-9 ._]+)} |
241 # Delimiter and a braced local option
242 {(?P<braced_double>[-a-z0-9 ._]+:[-a-z0-9 ._]+)} |
243 # Delimiter and a braced fully
244 # qualified option (that is, with
245 # explicit section).
246 {(?P<invalid>[^}]*}) # Other ill-formed delimiter exprs.
247 )
248 """, re.IGNORECASE | re.VERBOSE)
249
250 def __init__(self, source):
251 self.source = source
252 self.template = open(source).read()
253
254 def _get_colno_lineno(self, i):
255 lines = self.template[:i].splitlines(True)
256 if not lines:
257 colno = 1
258 lineno = 1
259 else:
260 colno = i - len(''.join(lines[:-1]))
261 lineno = len(lines)
262 return colno, lineno
263
264 def _get(self, options, section, option, seen, start):
265 value = options.get(option, None, seen)
266 if value is None:
267 colno, lineno = self._get_colno_lineno(start)
268 raise zc.buildout.buildout.MissingOption(
269 "Option '%s:%s', referenced in line %d, col %d of %s, "
270 "does not exist." %
271 (section, option, lineno, colno, self.source))
272 return value
273
274 def substitute(self, recipe, seen):
275 # Helper function for .sub()
276 def convert(mo):
277 # Check the most common path first.
278 option = mo.group('braced_single')
279 if option is not None:
280 val = self._get(recipe.options, recipe.name, option, seen,
281 mo.start('braced_single'))
282 # We use this idiom instead of str() because the latter will
283 # fail if val is a Unicode containing non-ASCII characters.
284 return '%s' % (val,)
285 double = mo.group('braced_double')
286 if double is not None:
287 section, option = double.split(':')
288 val = self._get(recipe.buildout[section], section, option, seen,
289 mo.start('braced_double'))
290 return '%s' % (val,)
291 escaped = mo.group('escaped')
292 if escaped is not None:
293 return '${%s}' % (escaped,)
294 invalid = mo.group('invalid')
295 if invalid is not None:
296 colno, lineno = self._get_colno_lineno(mo.start('invalid'))
297 raise ValueError(
298 'Invalid placeholder %r in line %d, col %d of %s' %
299 (mo.group('invalid'), lineno, colno, self.source))
300 raise ValueError('Unrecognized named group in pattern',
301 self.pattern) # programmer error, AFAICT
302 return self.pattern.sub(convert, self.template)
303
224304
=== modified file 'z3c/recipe/filetemplate/tests.py'
--- z3c/recipe/filetemplate/tests.py 2009-04-30 17:56:10 +0000
+++ z3c/recipe/filetemplate/tests.py 2009-07-09 18:20:56 +0000
@@ -12,12 +12,24 @@
12#12#
13##############################################################################13##############################################################################
1414
15import os
15import zc.buildout.testing16import zc.buildout.testing
16import zc.buildout.tests17import zc.buildout.tests
17from zope.testing import doctest18from zope.testing import doctest
1819
20def write_and_wait(dir, *args):
21 path = os.path.join(dir, *(args[:-1]))
22 original = os.stat(path).st_mtime
23 while os.stat(path).st_mtime == original:
24 f = open(path, 'w')
25 f.write(args[-1])
26 f.flush()
27 os.fsync(f.fileno())
28 f.close()
29
19def setUp(test):30def setUp(test):
20 zc.buildout.tests.easy_install_SetUp(test)31 zc.buildout.tests.easy_install_SetUp(test)
32 test.globs['write_and_wait'] = write_and_wait
21 zc.buildout.testing.install_develop('z3c.recipe.filetemplate', test)33 zc.buildout.testing.install_develop('z3c.recipe.filetemplate', test)
2234
23def test_suite():35def test_suite():
2436
=== modified file 'z3c/recipe/filetemplate/tests.txt'
--- z3c/recipe/filetemplate/tests.txt 2009-04-30 21:54:08 +0000
+++ z3c/recipe/filetemplate/tests.txt 2009-07-10 02:59:42 +0000
@@ -148,11 +148,12 @@
148 ... files = missing.txt148 ... files = missing.txt
149 ... """)149 ... """)
150 150
151 >>> print system(buildout)151 >>> print system(buildout) # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
152 Installing missing.152 Installing missing.
153 While:153 While:
154 Installing missing.154 Installing missing.
155 Error: Referenced option does not exist: missing world155 Error: Option 'missing:world', referenced in line 2, col 8 of
156 .../sample-buildout/missing.txt.in, does not exist.
156157
157No changes means just an update158No changes means just an update
158-------------------------------159-------------------------------

Subscribers

People subscribed via source and target branches

to all changes: