Skip to content

Commit

Permalink
Make Path and .path() expand users and environment variables.
Browse files Browse the repository at this point in the history
Fixes #3
  • Loading branch information
jamadden committed Sep 25, 2018
1 parent 3d013be commit c7d9c6c
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 7 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -16,6 +16,11 @@ Changes

- Drop support for ``python setup.py test``.

- Make ``zope.configuration.fields.Path`` and
``zope.configuration.config.ConfigurationContext`` expand
environment variables and expand user home directories in paths. See
`issue <https://github.com/zopefoundation/zope.configuration/issues/3>`_.

- Fix resolving names from a Python 2 package whose ``__init__.py`` has
unicode elements in ``__all__``.

Expand Down
25 changes: 25 additions & 0 deletions docs/api/fields.rst
Expand Up @@ -203,6 +203,16 @@
>>> n.split(os.sep)
['', 'a', 'b']

Environment variables are expanded:

.. doctest::

>>> os.environ['path-test'] = '42'
>>> with_env = os.path.join(os.sep, u'a', u'${path-test}')
>>> n = field.fromUnicode(with_env)
>>> n.split(os.sep)
['', 'a', '42']

Now try a relative path:

.. doctest::
Expand All @@ -212,6 +222,21 @@
>>> n.split(os.sep)
['', 'faux', 'context', 'a', 'b']

The current user is expanded (these are implicitly relative paths):

.. doctest::

>>> old_home = os.environ.get('HOME')
>>> os.environ['HOME'] = os.path.join(os.sep, 'HOME')
>>> n = field.fromUnicode('~')
>>> n.split(os.sep)
['', 'HOME']
>>> if old_home:
... os.environ['HOME'] = old_home
... else:
... del os.environ['HOME']


.. autoclass:: Bool
:members:
:member-order: bysource
Expand Down
5 changes: 3 additions & 2 deletions src/zope/configuration/config.py
Expand Up @@ -31,6 +31,7 @@
from zope.configuration.interfaces import IGroupingContext
from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import GlobalObject
from zope.configuration.fields import PathProcessor
from zope.configuration._compat import builtins
from zope.configuration._compat import reraise
from zope.configuration._compat import string_types
Expand Down Expand Up @@ -185,9 +186,9 @@ def resolve(self, dottedname):
def path(self, filename):
""" Compute package-relative paths.
"""
filename = os.path.normpath(filename)
filename, needs_processing = PathProcessor.expand(filename)

if os.path.isabs(filename):
if not needs_processing:
return filename

# Got a relative path, combine with base path.
Expand Down
33 changes: 28 additions & 5 deletions src/zope/configuration/fields.py
Expand Up @@ -120,19 +120,42 @@ def fromUnicode(self, u):

return values

class PathProcessor(object):
# Internal helper for manipulations on paths

@classmethod
def expand(cls, filename):
# Perform the expansions we want to have done. Returns a
# tuple: (path, needs_processing) If the second value is true,
# further processing should be done (the path isn't fully
# resolved); if false, the path should be used as is

filename = filename.strip()
# expanding a ~ at the front should generally result
# in an absolute path.
filename = os.path.expanduser(filename)
filename = os.path.expandvars(filename)
if os.path.isabs(filename):
return os.path.normpath(filename), False
return filename, True


@implementer(IFromUnicode)
class Path(Text):
"""A file path name, which may be input as a relative path
Input paths are converted to absolute paths and normalized.
.. versionchanged:: 4.2.0
Start expanding home directories and environment variables.
"""
def fromUnicode(self, u):
u = u.strip()
if os.path.isabs(u):
return os.path.normpath(u)

return self.context.path(u)
def fromUnicode(self, value):
filename, needs_processing = PathProcessor.expand(value)
if needs_processing:
filename = self.context.path(filename)

return filename


@implementer(IFromUnicode)
Expand Down
6 changes: 6 additions & 0 deletions src/zope/configuration/interfaces.py
Expand Up @@ -64,6 +64,12 @@ def path(filename):
If the filename is relative to the package, then the returned
name will include the package path, otherwise, the original
file name is returned.
Environment variables in the path are expanded, and if the path
begins with the username marker (~), that is expanded as well.
.. versionchanged:: 4.2.0
Start expanding home directories and environment variables.
"""

def checkDuplicate(filename):
Expand Down
25 changes: 25 additions & 0 deletions src/zope/configuration/tests/test_config.py
Expand Up @@ -163,6 +163,31 @@ class stub:
c.package = stub()
self.assertTrue(os.path.isabs(c.path('y/z')))

def test_path_expand_filenames(self):
import os
c = self._makeOne()
os.environ['path-test'] = '42'
try:
path = c.path(os.path.join(os.sep, '${path-test}'))
self.assertEqual(path, os.path.join(os.sep, '42'))
finally:
del os.environ['path-test']

def test_path_expand_user(self):
import os
c = self._makeOne()
old_home = os.environ.get('HOME')
# HOME should be absolute
os.environ['HOME'] = os.path.join(os.sep, 'HOME')
try:
path = c.path('~')
self.assertEqual(path, os.path.join(os.sep, 'HOME'))
finally: # pragma: no cover
if old_home:
os.environ['HOME'] = old_home
else:
del os.environ['HOME']

def test_checkDuplicate_miss(self):
import os
c = self._makeOne()
Expand Down

0 comments on commit c7d9c6c

Please sign in to comment.