Skip to content

Commit

Permalink
Merge pull request #446 from python/feature/jaraco-path-build
Browse files Browse the repository at this point in the history
Use jaraco.path for generating the record and files
  • Loading branch information
jaraco authored Apr 10, 2023
2 parents 64ae8f6 + 142d0dd commit 5811d73
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 18 deletions.
104 changes: 104 additions & 0 deletions tests/_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# from jaraco.path 3.5

import functools
import pathlib
from typing import Dict, Union

try:
from typing import Protocol, runtime_checkable
except ImportError: # pragma: no cover
# Python 3.7
from typing_extensions import Protocol, runtime_checkable # type: ignore


FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore


@runtime_checkable
class TreeMaker(Protocol):
def __truediv__(self, *args, **kwargs):
... # pragma: no cover

def mkdir(self, **kwargs):
... # pragma: no cover

def write_text(self, content, **kwargs):
... # pragma: no cover

def write_bytes(self, content):
... # pragma: no cover


def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker:
return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore


def build(
spec: FilesSpec,
prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore
):
"""
Build a set of files/directories, as described by the spec.
Each key represents a pathname, and the value represents
the content. Content may be a nested directory.
>>> spec = {
... 'README.txt': "A README file",
... "foo": {
... "__init__.py": "",
... "bar": {
... "__init__.py": "",
... },
... "baz.py": "# Some code",
... }
... }
>>> target = getfixture('tmp_path')
>>> build(spec, target)
>>> target.joinpath('foo/baz.py').read_text(encoding='utf-8')
'# Some code'
"""
for name, contents in spec.items():
create(contents, _ensure_tree_maker(prefix) / name)


@functools.singledispatch
def create(content: Union[str, bytes, FilesSpec], path):
path.mkdir(exist_ok=True)
build(content, prefix=path) # type: ignore


@create.register
def _(content: bytes, path):
path.write_bytes(content)


@create.register
def _(content: str, path):
path.write_text(content, encoding='utf-8')


class Recording:
"""
A TreeMaker object that records everything that would be written.
>>> r = Recording()
>>> build({'foo': {'foo1.txt': 'yes'}, 'bar.txt': 'abc'}, r)
>>> r.record
['foo/foo1.txt', 'bar.txt']
"""

def __init__(self, loc=pathlib.PurePosixPath(), record=None):
self.loc = loc
self.record = record if record is not None else []

def __truediv__(self, other):
return Recording(self.loc / other, self.record)

def write_text(self, content, **kwargs):
self.record.append(str(self.loc))

write_bytes = write_text

def mkdir(self, **kwargs):
return
13 changes: 13 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
from .py39compat import FS_NONASCII
from typing import Dict, Union

from . import _path


try:
from importlib import resources # type: ignore

Expand Down Expand Up @@ -266,6 +269,16 @@ def build_files(file_defs, prefix=pathlib.Path()):
f.write(DALS(contents))


def build_record(file_defs):
return ''.join(f'{name},,\n' for name in record_names(file_defs))


def record_names(file_defs):
recording = _path.Recording()
_path.build(file_defs, recording)
return recording.record


class FileBuilder:
def unicode_filename(self):
return FS_NONASCII or self.skip("File system does not support non-ascii.")
Expand Down
42 changes: 24 additions & 18 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import unittest
import importlib
import importlib_metadata
import itertools
import pyfakefs.fake_filesystem_unittest as ffs

from . import fixtures
Expand Down Expand Up @@ -328,25 +329,30 @@ def test_packages_distributions_all_module_types(self):
Test top-level modules detected on a package without 'top-level.txt'.
"""
suffixes = importlib.machinery.all_suffixes()
fixtures.build_files(
{
'all_distributions-1.0.0.dist-info': {
'METADATA': """
Name: all_distributions
Version: 1.0.0
""",
'RECORD': 'all_distributions-1.0.0.dist-info/METADATA\n'
+ ''.join(
f'importable-name {i}{suffix},,\n'
f'in_namespace_{i}/mod{suffix},,\n'
f'in_package_{i}/__init__.py,,\n'
f'in_package_{i}/mod{suffix},,\n'
for i, suffix in enumerate(suffixes)
),
},
},
prefix=self.site_dir,
metadata = dict(
METADATA="""
Name: all_distributions
Version: 1.0.0
""",
)
files = {
'all_distributions-1.0.0.dist-info': metadata,
}
for i, suffix in enumerate(suffixes):
files.update(
{
f'importable-name {i}{suffix}': '',
f'in_namespace_{i}': {
f'mod{suffix}': '',
},
f'in_package_{i}': {
'__init__.py': '',
f'mod{suffix}': '',
},
}
)
metadata.update(RECORD=fixtures.build_record(files))
fixtures.build_files(files, prefix=self.site_dir)

distributions = packages_distributions()

Expand Down

0 comments on commit 5811d73

Please sign in to comment.