Skip to content

Commit

Permalink
resolve raw
Browse files Browse the repository at this point in the history
  • Loading branch information
nmichlo committed May 14, 2024
1 parent 7d72911 commit 5b56cdc
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 22 deletions.
74 changes: 59 additions & 15 deletions pydependence/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class OutputModeEnum(str, Enum):

class _Output(_ResolveRules, extra="forbid"):
# resolve
scope: str
scope: Optional[str] = None
start_scope: Optional[str] = None

# raw requirements / imports that are mapped
Expand All @@ -127,40 +127,77 @@ def get_output_name(self) -> str:
return self.output_name
elif self.start_scope is not None:
return self.start_scope
else:
elif self.scope is not None:
return self.scope
else:
raise ValueError(f"output_name cannot be determined, please set output_name, start_scope, or scope for: {self}")

def get_manual_imports(self):
if not self.raw:
return []
return [ManualImportInfo.from_target(r) for r in self.raw]

def _write_requirements(self, mapped_requirements: OutMappedRequirements) -> None:
raise NotImplementedError(
f"tried to write imports for {repr(self.get_output_name())}, write_imports not implemented for {self.__class__.__name__}"
)
@pydantic.model_validator(mode="after")
@classmethod
def _validate_model(cls, v):
if v.start_scope is not None:
if v.scope is None:
raise ValueError(f"start_scope is set, but scope is not set for: {v}")
return v

def resolve_generate_and_write_requirements(
def get_resolved_imports(
self,
loaded_scopes: "LoadedScopes",
requirements_mapper: RequirementsMapper,
) -> None:
resolved_imports = loaded_scopes[self.scope].resolve_imports(
start_scope=loaded_scopes[self.start_scope] if self.start_scope else None,
):
if not self.scope:
return []
# * normal scope
if self.scope not in loaded_scopes:
raise ValueError(
f"scope {repr(self.scope)} does not exist, must be one of: {loaded_scopes.sorted_names}"
)
else:
scope = loaded_scopes[self.scope]
# * start scope
start_scope = None
if self.start_scope:
if self.start_scope not in loaded_scopes:
raise ValueError(
f"start_scope {repr(self.start_scope)} does not exist, must be one of: {loaded_scopes.sorted_names}"
)
else:
start_scope = loaded_scopes[self.start_scope]
# * resolve imports
return scope.resolve_imports(
start_scope=start_scope,
visit_lazy=self.visit_lazy,
exclude_unvisited=self.exclude_unvisited,
exclude_in_search_space=self.exclude_in_search_space,
exclude_builtins=self.exclude_builtins,
)

def resolve_generate_and_write_requirements(
self,
loaded_scopes: "LoadedScopes",
requirements_mapper: RequirementsMapper,
) -> None:
resolved_imports = self.get_resolved_imports(loaded_scopes=loaded_scopes)
manual_imports = self.get_manual_imports()
mapped_requirements = requirements_mapper.generate_output_requirements(
imports=resolved_imports + self.get_manual_imports(),
imports=resolved_imports + manual_imports,
requirements_env=self.env,
strict=self.strict_requirements_map,
resolver_name=self.get_output_name(),
)
self._write_requirements(
mapped_requirements=mapped_requirements,
)

def _write_requirements(self, mapped_requirements: OutMappedRequirements) -> None:
raise NotImplementedError(
f"tried to write imports for {repr(self.get_output_name())}, write_imports not implemented for {self.__class__.__name__}"
)


class _OutputRequirements(_Output):
output_mode: Literal[OutputModeEnum.requirements]
Expand Down Expand Up @@ -433,7 +470,7 @@ def __contains__(self, item):
def __getitem__(self, item: str) -> ModulesScope:
if item not in self._scopes:
raise UndefinedScopeError(
f"scope {repr(item)} is not defined, must be one of: {sorted(self._scopes.keys())}"
f"scope {repr(item)} is not defined, must be one of: {self.sorted_names}"
)
return self._scopes[item]

Expand All @@ -443,6 +480,10 @@ def __setitem__(self, key, value):
raise ValueError(f"scope {repr(key)} is already defined!")
self._scopes[key] = value

@property
def sorted_names(self) -> List[str]:
return sorted(self._scopes.keys())


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #
# CONFIG - ROOT #
Expand Down Expand Up @@ -602,13 +643,16 @@ def write_all_outputs(self, loaded_scopes: "LoadedScopes"):

# check that the scopes exists
for output in self.resolvers:
if output.scope is None:
assert output.start_scope is None
continue
if output.scope not in loaded_scopes:
raise ValueError(
f"output scope {repr(output.scope)} does not exist! Are you sure it has been defined?"
f"output scope {repr(output.scope)} does not exist! Are you sure it has been defined? Available scopes: {loaded_scopes.sorted_names}"
)
if output.start_scope and output.start_scope not in loaded_scopes:
raise ValueError(
f"output start_scope {repr(output.start_scope)} does not exist! Are you sure it has been defined?"
f"output start_scope {repr(output.start_scope)} does not exist! Are you sure it has been defined? Available scopes: {loaded_scopes.sorted_names}"
)

# make the mapper
Expand Down
12 changes: 10 additions & 2 deletions pydependence/_core/requirements_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ def to_output_requirement(self):
@dataclasses.dataclass
class MappedRequirements:
requirements: Dict[str, MappedRequirement] # k == v.requirement
resolver_name: Optional[str] = None

def get_sorted_requirements(self) -> List[MappedRequirement]:
return sorted(
Expand All @@ -202,7 +203,8 @@ def to_output_requirements(self):
requirements=[
requirement_info.to_output_requirement()
for requirement_info in self.get_sorted_requirements()
]
],
resolver_name=self.resolver_name,
)


Expand Down Expand Up @@ -373,6 +375,7 @@ def generate_mapped_requirements(
requirements_env: "Optional[str]" = None,
strict: bool = False,
raw: List[str] = None,
resolver_name: Optional[str] = None,
) -> "MappedRequirements":
"""
Map imports to requirements, returning the imports grouped by the requirement.
Expand All @@ -383,7 +386,10 @@ def generate_mapped_requirements(
raise NotImplementedError("raw imports are not yet supported, TODO!!!")

# group imports by requirement
r = MappedRequirements(requirements={})
r = MappedRequirements(
requirements={},
resolver_name=resolver_name,
)
errors = []
for imp in imports:
# 1. map requirements
Expand Down Expand Up @@ -454,6 +460,7 @@ def generate_output_requirements(
*,
requirements_env: "Optional[str]" = None,
strict: bool = False,
resolver_name: Optional[str] = None,
) -> "OutMappedRequirements":
"""
:raises NoConfiguredRequirementMappingError: if no requirement is found for any import, but only if strict mode is enabled.
Expand All @@ -463,6 +470,7 @@ def generate_output_requirements(
imports,
requirements_env=requirements_env,
strict=strict,
resolver_name=resolver_name,
)
# 2. generate output requirements lists
return r.to_output_requirements()
Expand Down
15 changes: 12 additions & 3 deletions pydependence/_core/requirements_out.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import dataclasses
from typing import List, Tuple
from typing import List, Optional, Tuple

# ========================================================================= #
# REQUIREMENTS MAPPER #
Expand Down Expand Up @@ -47,8 +47,17 @@ def get_sources_string(
@dataclasses.dataclass
class OutMappedRequirements:
requirements: List[OutMappedRequirement]
resolver_name: Optional[str] = None

_AUTOGEN_NOTICE = "[AUTOGEN] by pydependence **DO NOT EDIT** [AUTOGEN]"
_AUTOGEN_NOTICE_NAMED = "[AUTOGEN] by pydependence resolver {resolver_name} **DO NOT EDIT** [AUTOGEN]"

@property
def autogen_notice(self) -> str:
if self.resolver_name is None:
return self._AUTOGEN_NOTICE
else:
return self._AUTOGEN_NOTICE_NAMED.format(resolver_name=repr(self.resolver_name))

def _get_debug_struct(self) -> "List[Tuple[str, List[str]]]":
return [
Expand All @@ -66,7 +75,7 @@ def as_requirements_txt(
) -> str:
lines = []
if notice:
lines.append(self._AUTOGEN_NOTICE)
lines.append(self.autogen_notice)
for req in self.requirements:
# add requirement
lines.append(f"{req.requirement}")
Expand Down Expand Up @@ -97,7 +106,7 @@ def as_toml_array(
if notice:
array.add_line(
indent=" " * (indent_size * 1),
comment=self._AUTOGEN_NOTICE,
comment=self.autogen_notice,
)
for req in self.requirements:
# add requirement & compact sources
Expand Down
18 changes: 16 additions & 2 deletions tests/test-packages/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ versions = [
resolvers = [
{output_mode='dependencies', scope='all'},
{output_mode='optional-dependencies', scope='all', output_name='all', visit_lazy=true},
{output_mode='optional-dependencies', output_name='test', raw=['pytest', 'pytest-cov']},
{output_mode='optional-dependencies', output_name='dev', raw=['pre-commit']},
]

# collections of packages and dependencies that will then be resolved.
Expand All @@ -38,7 +40,7 @@ limit = "A.a3"
[project]

dependencies = [
# [AUTOGEN] by pydependence **DO NOT EDIT** [AUTOGEN]
# [AUTOGEN] by pydependence resolver 'all' **DO NOT EDIT** [AUTOGEN]
"asdf",
# ← A.a1
# ← A.a2
Expand All @@ -56,7 +58,7 @@ dependencies = [
[project.optional-dependencies]

all = [
# [AUTOGEN] by pydependence **DO NOT EDIT** [AUTOGEN]
# [AUTOGEN] by pydependence resolver 'all' **DO NOT EDIT** [AUTOGEN]
"asdf",
# ← A.a1
# ← A.a2
Expand All @@ -76,3 +78,15 @@ all = [
"package",
# ← t_ast_parser
]
test = [
# [AUTOGEN] by pydependence resolver 'test' **DO NOT EDIT** [AUTOGEN]
"pytest",
# ← <raw: pytest>
"pytest-cov",
# ← <raw: pytest-cov>
]
dev = [
# [AUTOGEN] by pydependence resolver 'dev' **DO NOT EDIT** [AUTOGEN]
"pre-commit",
# ← <raw: pre-commit>
]

0 comments on commit 5b56cdc

Please sign in to comment.