Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve site package pathing #781

Draft
wants to merge 20 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2e952ff
Improve site-package pathing to path all shuffix of file
WangGithubUser Aug 21, 2023
3810dd6
Update unittest
WangGithubUser Aug 21, 2023
9e7de0f
Add unittest for new feature
WangGithubUser Aug 21, 2023
631e427
Fix FileExistsError in unittest
WangGithubUser Aug 21, 2023
5fdddb4
Fix type errors
WangGithubUser Aug 21, 2023
ee67824
Fix type errors
WangGithubUser Aug 21, 2023
94749be
Update pattern to filter out path like `__pycache__/valid_before_this…
WangGithubUser Aug 22, 2023
42baee2
Improve unittest
WangGithubUser Aug 22, 2023
d6fac87
Improve handing on not exists site-package
WangGithubUser Aug 22, 2023
7e6afcb
Delete unnecessary condition
WangGithubUser Sep 8, 2023
5c0f85c
Fix element adding in func add_if_exists
WangGithubUser Sep 9, 2023
1608b6a
Add unittest for process_raw_element
WangGithubUser Sep 9, 2023
f5a3d0a
Rewrite unittest with module tempfile and func `ensure_directories_ex…
WangGithubUser Sep 10, 2023
4cafef7
Rewrite client/backend_arguments.py's func get_checked_directory_allo…
WangGithubUser Sep 10, 2023
179192e
Fix unittest
WangGithubUser Sep 10, 2023
8717c8a
Add pyre-fixme to avoid false positive type error
WangGithubUser Sep 10, 2023
6bedb96
Use comment to specify the re.Pattern type
WangGithubUser Sep 15, 2023
fc25a8c
Fix typos
WangGithubUser Sep 15, 2023
809571b
Fix incompatible return type in get_checked_directory_allowlist func
WangGithubUser Sep 15, 2023
1edfbc1
Use comment to specify type of not_toplevel_patterns
WangGithubUser Sep 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/backend_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def serialize(self) -> Dict[str, object]:
}

def get_checked_directory_allowlist(self) -> Set[str]:
return {element.path() for element in self.elements}
return {element.path() for element in self.elements if element is not None}
Fixed Show fixed Hide fixed
WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved

def cleanup(self) -> None:
pass
Expand Down Expand Up @@ -98,7 +98,7 @@ def serialize(self) -> Dict[str, object]:
}

def get_checked_directory_allowlist(self) -> Set[str]:
return {element.path() for element in self.elements}
return {element.path() for element in self.elements if element is not None}
Fixed Show fixed Hide fixed
WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved

def cleanup(self) -> None:
pass
Expand Down
78 changes: 64 additions & 14 deletions client/configuration/search_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,22 @@
import glob
import logging
import os
from typing import Dict, Iterable, List, Sequence, Union
import re
from typing import Dict, Iterable, List, Sequence, Tuple, Union

from .. import filesystem
from . import exceptions

LOG: logging.Logger = logging.getLogger(__name__)

dist_info_in_root: Dict[str, List[str]] = {}

# pyre-fixme[5]: Globally accessible variable `_site_filter` has type `re.Pattern[str]` but no type is specified.
_site_filter = re.compile(r".*-([0-99]\.)*dist-info")
Fixed Show fixed Hide fixed

# pyre-fixme[5]: Globally accessible variable `_PYCACHE` has type `re.Pattern[str]` but no type is specified.
_PYCACHE = re.compile("__pycache__(/)*.*")
WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved


def _expand_relative_root(path: str, relative_root: str) -> str:
if not path.startswith("//"):
Expand All @@ -35,11 +44,11 @@ def _expand_relative_root(path: str, relative_root: str) -> str:

class Element(abc.ABC):
@abc.abstractmethod
def path(self) -> str:
def path(self) -> Union[str, None]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Optional[str]

could you explain the reasoning and semantic differences for making this optional in the summary?

raise NotImplementedError()

@abc.abstractmethod
def command_line_argument(self) -> str:
def command_line_argument(self) -> Union[str, None]:
raise NotImplementedError()


Expand Down Expand Up @@ -72,15 +81,55 @@ class SitePackageElement(Element):
package_name: str
is_toplevel_module: bool = False

def package_path(self) -> str:
module_suffix = ".py" if self.is_toplevel_module else ""
return self.package_name + module_suffix
def package_path(self) -> Union[str, None]:
if not self.is_toplevel_module:
return self.package_name

def path(self) -> str:
return os.path.join(self.site_root, self.package_path())
this_pkg_filter = re.compile(
r"{}-([0-99]\.)*dist-info(/)*.*".format(self.package_name)
)

def command_line_argument(self) -> str:
return self.site_root + "$" + self.package_path()
if self.site_root not in dist_info_in_root:
dist_info_in_root[self.site_root] = []
for directory in os.listdir(self.site_root):
if _site_filter.fullmatch(directory) is not None:
dist_info_in_root[self.site_root].append(directory)

for directory in dist_info_in_root[self.site_root]:
if this_pkg_filter.fullmatch(directory):
dist_info_path = f"{self.site_root}/{directory}"
break
else:
return None

not_toplevel_patterns: Tuple[re.Pattern[str], re.Pattern[str]] = (
this_pkg_filter,
_PYCACHE,
)

# pyre-fixme[61]: Local variable `dist_info_path` is undefined, or not always defined.
with open(file=f"{dist_info_path}/RECORD", mode="r") as record:
WangGithubUser marked this conversation as resolved.
Fixed
Show resolved Hide resolved
WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved
for line in record.readlines():
file_name = line.split(",")[0]
for pattern in not_toplevel_patterns:
if pattern.fullmatch(file_name):
break
else:
return file_name

WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved
return None

def path(self) -> Union[str, None]:
excepted_package_path: Union[str, None] = self.package_path()
WangGithubUser marked this conversation as resolved.
Show resolved Hide resolved
if excepted_package_path is None:
return None
return os.path.join(self.site_root, excepted_package_path)
Fixed Show fixed Hide fixed

def command_line_argument(self) -> Union[str, None]:
excepted_package_path: Union[str, None] = self.package_path()
if excepted_package_path is None:
return None
return self.site_root + "$" + excepted_package_path
Fixed Show fixed Hide fixed


class RawElement(abc.ABC):
Expand Down Expand Up @@ -223,10 +272,11 @@ def process_raw_elements(
elements: List[Element] = []

def add_if_exists(element: Element) -> bool:
if os.path.exists(element.path()):
elements.append(element)
return True
return False
excepted_path = element.path()
if excepted_path is None or not os.path.exists(excepted_path):
return False
elements.append(element)
return True

for raw_element in raw_elements:
expanded_raw_elements = raw_element.expand_glob()
Expand Down
75 changes: 59 additions & 16 deletions client/configuration/tests/search_path_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,45 @@ def test_create_raw_element(self) -> None:
)

def test_path(self) -> None:
self.assertEqual(SimpleElement("foo").path(), "foo")
self.assertEqual(SubdirectoryElement("foo", "bar").path(), "foo/bar")
self.assertEqual(SitePackageElement("foo", "bar").path(), "foo/bar")
with tempfile.TemporaryDirectory() as temp_root:
ensure_directories_exists(Path(temp_root), ("foo", "foo/bar"))
self.assertEqual(SimpleElement("foo").path(), "foo")
self.assertEqual(SubdirectoryElement("foo", "bar").path(), "foo/bar")
self.assertEqual(
SitePackageElement(f"{temp_root}/foo", "bar").path(),
f"{temp_root}/foo/bar",
)

def test_command_line_argument(self) -> None:
self.assertEqual(SimpleElement("foo").command_line_argument(), "foo")
self.assertEqual(
SubdirectoryElement("foo", "bar").command_line_argument(),
"foo$bar",
)
self.assertEqual(
SitePackageElement("foo", "bar").command_line_argument(),
"foo$bar",
)
self.assertEqual(
SitePackageElement("foo", "bar", True).command_line_argument(),
"foo$bar.py",
)
with tempfile.TemporaryDirectory() as temp_root:
ensure_directories_exists(Path(temp_root), ("foo", "foo/bar"))
self.assertEqual(SimpleElement("foo").command_line_argument(), "foo")
self.assertEqual(
SubdirectoryElement("foo", "bar").command_line_argument(),
"foo$bar",
)
self.assertEqual(
SitePackageElement(f"{temp_root}/foo", "bar").command_line_argument(),
f"{temp_root}/foo$bar",
)

with tempfile.TemporaryDirectory() as temp_root:
ensure_directories_exists(
Path(temp_root), ("foo", "foo/bar-1.0.0.dist-info")
)
Path.touch(Path(f"{temp_root}/foo/bar.py"))

with open(
f"{temp_root}/foo/bar-1.0.0.dist-info/RECORD", "w", encoding="UTF-8"
) as f:
f.write("bar.py,,")

self.assertEqual(
SitePackageElement(
f"{temp_root}/foo", "bar", True
).command_line_argument(),
f"{temp_root}/foo$bar.py",
)

def test_expand_global_root(self) -> None:
self.assertEqual(
Expand Down Expand Up @@ -241,3 +262,25 @@ def test_process_required_raw_elements_site_package_nonexistence(self) -> None:
site_roots=[],
required=True,
)

def test_toplevel_module_not_pyfile(self) -> None:
with tempfile.TemporaryDirectory() as temp_root:
ensure_directories_exists(
Path(temp_root), ("foo", "foo/bar-1.0.0.dist-info")
)
Path.touch(Path(f"{temp_root}/foo/bar.so"))

with open(
f"{temp_root}/foo/bar-1.0.0.dist-info/RECORD", "w", encoding="UTF-8"
) as f:
f.write("bar.so,,")

self.assertEqual(
SitePackageElement(f"{temp_root}/foo", "bar", True).path(), "foo/bar.so"
)
self.assertEqual(
process_raw_elements(
[SitePackageRawElement("bar", True)], [f"{temp_root}/foo"]
),
[SitePackageElement(f"{temp_root}/foo", "bar", True)],
)
20 changes: 14 additions & 6 deletions client/tests/backend_arguments_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import shutil
import tempfile
from pathlib import Path
from typing import Iterable, Tuple
Expand Down Expand Up @@ -576,15 +577,22 @@ def test_get_source_path__confliciting_source_specified(self) -> None:
)

def test_get_checked_directory_for_simple_source_path(self) -> None:
Path.mkdir(Path("super"))
Path.mkdir(Path("super/slash"))

element0 = search_path.SimpleElement("ozzie")
element1 = search_path.SubdirectoryElement("diva", "flea")
element2 = search_path.SitePackageElement("super", "slash")
self.assertCountEqual(
SimpleSourcePath(
[element0, element1, element2, element0]
).get_checked_directory_allowlist(),
[element0.path(), element1.path(), element2.path()],
)

try:
self.assertCountEqual(
SimpleSourcePath(
[element0, element1, element2, element0]
).get_checked_directory_allowlist(),
[element0.path(), element1.path(), element2.path()],
)
finally:
shutil.rmtree("super")

def test_get_checked_directory_for_buck_source_path(self) -> None:
self.assertCountEqual(
Expand Down