Skip to content

Commit

Permalink
Merge pull request #99 from gauge-sh/fix-root-boundary
Browse files Browse the repository at this point in the history
0.5.1 - Fix bugs related to root boundary
  • Loading branch information
emdoyle authored Jun 3, 2024
2 parents f3a4674 + a11fbb2 commit 0059b48
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tach"
version = "0.5.0"
version = "0.5.1"
edition = "2021"

[lib]
Expand Down
2 changes: 1 addition & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ If you use the [pre-commit framework](https://github.com/pre-commit/pre-commit),
```yaml
repos:
- repo: https://github.com/gauge-sh/tach
rev: v0.5.0 # change this to the latest tag!
rev: v0.5.1 # change this to the latest tag!
hooks:
- id: tach
# args: ["--root=backend_root"]
Expand Down
2 changes: 1 addition & 1 deletion lsp/vscode/bundled/tool/lsp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def update_sys_path(path_to_add: str, strategy: str) -> None:
MAX_WORKERS = 5
# TODO: Centralize version
LSP_SERVER = server.LanguageServer(
name="Tach", version="0.5.0", max_workers=MAX_WORKERS
name="Tach", version="0.5.1", max_workers=MAX_WORKERS
)


Expand Down
2 changes: 1 addition & 1 deletion lsp/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "tach",
"displayName": "Tach",
"description": "Linting support for python files using `tach`.",
"version": "0.5.0",
"version": "0.5.1",
"preview": true,
"serverInfo": {
"name": "Tach",
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "tach"
version = "0.5.0"
version = "0.5.1"
authors = [
{ name="Caelean Barnes", email="[email protected]" },
{ name="Evan Doyle", email="[email protected]" },
Expand Down Expand Up @@ -61,7 +61,7 @@ runtime-evaluated-decorators = [
exempt-modules = ["typing", "typing_extensions"]

[tool.pyright]
include = ["python/tach", "lsp/vscode/src/test/python_tests", "lsp/vscode/bundled/tool"]
include = ["python/tach"]
exclude = ["**/__pycache__", ".venv"]
strict = ["python/tach"]
executionEnvironments = [
Expand Down
6 changes: 6 additions & 0 deletions python/tach/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class ModuleConfig(Config):
depends_on: list[str] = Field(default_factory=list)
strict: bool = False

@property
def mod_path(self) -> str:
if self.path == ROOT_MODULE_SENTINEL_TAG:
return "."
return self.path


def validate_root_path(path: str) -> str:
assert path == ROOT_MODULE_SENTINEL_TAG
Expand Down
8 changes: 7 additions & 1 deletion python/tach/core/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def fill(


def split_module_path(path: str) -> list[str]:
if not path:
if not path or path == ".":
return []
return path.split(".")

Expand All @@ -62,6 +62,9 @@ def __iter__(self):
return module_tree_iterator(self)

def get(self, path: str) -> ModuleNode | None:
if not path:
return None

node = self.root
parts = split_module_path(path)

Expand All @@ -73,6 +76,9 @@ def get(self, path: str) -> ModuleNode | None:
return node if node.is_end_of_path else None

def insert(self, config: ModuleConfig, path: str, interface_members: list[str]):
if not path:
raise ValueError("Cannot insert module with empty path.")

node = self.root
parts = split_module_path(path)

Expand Down
7 changes: 7 additions & 0 deletions python/tach/filesystem/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from tach import errors
from tach.colors import BCOLORS
from tach.constants import ROOT_MODULE_SENTINEL_TAG


@dataclass
Expand Down Expand Up @@ -270,6 +271,12 @@ def module_to_file_path_no_members(module_path: str) -> Path | None:
This resolves a dotted Python module path ('a.b.c')
into a Python file path or a Python package __init__.py
"""
if module_path == ROOT_MODULE_SENTINEL_TAG:
root_path = Path("__init__.py")
if root_path.exists():
return root_path
return None

base_path = module_path.replace(".", os.sep)
pyfile_path = Path(f"{base_path}.py")
init_py_path = Path(base_path).joinpath("__init__.py")
Expand Down
5 changes: 5 additions & 0 deletions python/tach/interactive/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ def _build_subtree(
# Only interested in Python files
continue

if os.path.basename(entry_path) == "__init__.py":
# __init__.py does not have a unique module path from its containing package
# so users should not be able to mark it as a standalone module
continue

# Adding a trailing slash lets us match 'tests/' as an exclude pattern
entry_path_for_matching = f"{fs.canonical(entry_path)}/"
if exclude_paths is not None and any(
Expand Down
3 changes: 3 additions & 0 deletions python/tach/parsing/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ def parse_interface_members(path: str) -> list[str]:
Parse the members of __all__ in a given module
"""
file_path = fs.module_to_file_path_no_members(path)
if file_path is None:
return []

parsed_ast = fs.parse_ast(str(file_path))
interface_visitor = InterfaceVisitor()
interface_visitor.visit(parsed_ast)
Expand Down
18 changes: 17 additions & 1 deletion python/tach/parsing/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,28 @@
from tach.core import ModuleConfig


def find_duplicate_modules(modules: list[ModuleConfig]) -> list[str]:
duplicate_module_paths: list[str] = []
seen: set[str] = set()
for module in modules:
if module.path in seen:
duplicate_module_paths.append(module.path)
continue
seen.add(module.path)
return duplicate_module_paths


def build_module_tree(modules: list[ModuleConfig]) -> ModuleTree:
duplicate_modules = find_duplicate_modules(modules)
if duplicate_modules:
raise ValueError(
f"Failed to build module tree. The following modules were defined more than once: {duplicate_modules}"
)
tree = ModuleTree()
for module in modules:
tree.insert(
config=module,
path=module.path,
path=module.mod_path,
interface_members=parse_interface_members(module.path),
)
return tree
7 changes: 3 additions & 4 deletions python/tests/test_module_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_get_nonexistent_path(module_tree):

def test_get_empty_path():
tree = ModuleTree()
assert tree.get("") is tree.root
assert tree.get("") is None


def test_get_actual_path(module_tree):
Expand All @@ -86,9 +86,8 @@ def test_get_actual_path(module_tree):

def test_insert_empty_path(test_config):
tree = ModuleTree()
tree.insert(test_config, "", [])
node = tree.get("")
assert node is not None and node.config == test_config
with pytest.raises(ValueError):
tree.insert(test_config, "", [])


def test_insert_single_level_path(test_config):
Expand Down

0 comments on commit 0059b48

Please sign in to comment.