Skip to content

Commit

Permalink
Add operators for the Python versions. (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
jesnie authored Aug 31, 2023
1 parent f79d298 commit 6423140
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 355 deletions.
1 change: 0 additions & 1 deletion TODO.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
* python_specifier and default_python as operators? (Remove from root?)
* Make a release a requirment?


Expand Down
8 changes: 8 additions & 0 deletions compreq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
AnySpecifierOperator,
AnySpecifierSet,
AnyVersion,
CompositeLazySpecifierSet,
EagerLazyRelease,
EagerLazyReleaseSet,
EagerLazyRequirementSet,
EagerLazySpecifierSet,
EagerLazyVersion,
LazyRelease,
LazyReleaseSet,
Expand Down Expand Up @@ -75,6 +77,7 @@
ceil_ver,
consistent_lower_bounds,
count,
default_python,
devreleases,
dist,
distribution,
Expand All @@ -88,6 +91,7 @@
min_ver,
minimum_ver,
prereleases,
python_specifier,
releases,
requirements,
specifier,
Expand Down Expand Up @@ -138,6 +142,7 @@
"Bounds",
"CeilLazyVersion",
"CompReq",
"CompositeLazySpecifierSet",
"Context",
"CountLazyReleaseSet",
"DefaultContext",
Expand All @@ -148,6 +153,7 @@
"EagerLazyRelease",
"EagerLazyReleaseSet",
"EagerLazyRequirementSet",
"EagerLazySpecifierSet",
"EagerLazyVersion",
"FloorLazyVersion",
"FtpDir",
Expand Down Expand Up @@ -199,6 +205,7 @@
"consistent_lower_bounds",
"count",
"create_venv",
"default_python",
"devreleases",
"dist",
"distribution",
Expand Down Expand Up @@ -230,6 +237,7 @@
"min_ver",
"minimum_ver",
"prereleases",
"python_specifier",
"releases",
"remove_venv",
"requirements",
Expand Down
3 changes: 2 additions & 1 deletion compreq/classifiers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Sequence

from compreq.lazy import AnyReleaseSet
from compreq.operators import python_specifier
from compreq.releases import ReleaseSet
from compreq.roots import CompReq

Expand All @@ -20,7 +21,7 @@ def add_version_str(s: str) -> None:
version_strs_list.append(s)

if python_releases is None:
python_releases = cr.python_specifier
python_releases = python_specifier()
assert python_releases is not None
python_releases = cr.resolve_release_set("python", python_releases)
assert isinstance(python_releases, ReleaseSet)
Expand Down
78 changes: 60 additions & 18 deletions compreq/lazy.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,13 @@ def get_lazy_release_set(release_set: AnyReleaseSet | None) -> LazyReleaseSet:
if isinstance(release_set, Requirement):
release_set = get_lazy_requirement(release_set)
if isinstance(release_set, LazyRequirement):
release_set = SpecifierLazyReleaseSet(
ProdLazyReleaseSet(AllLazyReleaseSet(release_set.distribution)), release_set.specifier
)
if release_set.specifier:
release_set = SpecifierLazyReleaseSet(
ProdLazyReleaseSet(AllLazyReleaseSet(release_set.distribution)),
release_set.specifier,
)
else:
release_set = ProdLazyReleaseSet(AllLazyReleaseSet(release_set.distribution))
if isinstance(release_set, Release):
release_set = EagerLazyRelease(release_set)
if isinstance(release_set, LazyRelease):
Expand Down Expand Up @@ -369,8 +373,7 @@ def get_lazy_specifier(specifier: AnySpecifier) -> LazySpecifier:
raise AssertionError(f"Unknown type of specifier: {type(specifier)}")


@dataclass(frozen=True)
class LazySpecifierSet:
class LazySpecifierSet(ABC):
"""
Strategy for computing a `SpecifierSet` in the context of a distribution.
Expand All @@ -381,12 +384,9 @@ class LazySpecifierSet:
"""

specifiers: frozenset[LazySpecifier]

@abstractmethod
async def resolve(self, context: DistributionContext) -> SpecifierSet:
"""Compute the `SpecifierSet`."""
specifiers = await asyncio.gather(*[s.resolve(context) for s in self.specifiers])
return SpecifierSet(",".join(str(s) for s in specifiers))

@overload
def __and__(self, rhs: AnySpecifierSet) -> LazySpecifierSet:
Expand All @@ -411,6 +411,24 @@ def __rand__(self, lhs: AnyRequirement) -> LazySpecifierSet | LazyRequirement:
return compose(lhs, self)


@dataclass(frozen=True)
class EagerLazySpecifierSet(LazySpecifierSet):
specifiers: frozenset[LazySpecifier]

async def resolve(self, context: DistributionContext) -> SpecifierSet:
specifiers = await asyncio.gather(*[s.resolve(context) for s in self.specifiers])
return SpecifierSet(",".join(str(s) for s in specifiers))


@dataclass(frozen=True)
class CompositeLazySpecifierSet(LazySpecifierSet):
specifiers: frozenset[LazySpecifierSet]

async def resolve(self, context: DistributionContext) -> SpecifierSet:
specifiers = await asyncio.gather(*[s.resolve(context) for s in self.specifiers])
return SpecifierSet(",".join(str(s) for s in specifiers))


AnySpecifierSet: TypeAlias = str | Specifier | LazySpecifier | SpecifierSet | LazySpecifierSet
"""Type alias for anything that can be converted to a `LazySpecifierSet`."""

Expand All @@ -422,9 +440,11 @@ def get_lazy_specifier_set(specifier_set: AnySpecifierSet) -> LazySpecifierSet:
if isinstance(specifier_set, Specifier):
specifier_set = get_lazy_specifier(specifier_set)
if isinstance(specifier_set, LazySpecifier):
specifier_set = LazySpecifierSet(frozenset([specifier_set]))
specifier_set = EagerLazySpecifierSet(frozenset([specifier_set]))
if isinstance(specifier_set, SpecifierSet):
specifier_set = LazySpecifierSet(frozenset(get_lazy_specifier(s) for s in specifier_set))
specifier_set = EagerLazySpecifierSet(
frozenset(get_lazy_specifier(s) for s in specifier_set)
)
if isinstance(specifier_set, LazySpecifierSet):
return specifier_set
raise AssertionError(f"Unknown type of specifier set: {type(specifier_set)}")
Expand Down Expand Up @@ -476,7 +496,7 @@ class LazyRequirement:
extras: frozenset[str]
"""Set of extras to install."""

specifier: LazySpecifierSet
specifier: LazySpecifierSet | None
"""
Specification of which versions of the distribution are valid.
Expand All @@ -487,7 +507,7 @@ class LazyRequirement:
"""Marker for specifying when this requirement should be used."""

def __post_init__(self) -> None:
assert (self.url is None) or not self.specifier.specifiers, (
assert (self.url is None) or (self.specifier is None), (
"A requirement cannot have both a url and a specifier."
f" Found: {self.url}, {self.specifier}."
)
Expand Down Expand Up @@ -516,7 +536,11 @@ async def resolve(self, context: Context) -> Requirement:
tokens.append(f"[{formatted_extras}]")

distribution_context = context.for_distribution(self.distribution)
specifier = await self.specifier.resolve(distribution_context)
specifier = (
(await self.specifier.resolve(distribution_context))
if self.specifier
else SpecifierSet()
)
tokens.append(str(specifier))

if self.url:
Expand All @@ -534,7 +558,7 @@ async def resolve(self, context: Context) -> Requirement:
distribution=None,
url=None,
extras=frozenset(),
specifier=LazySpecifierSet(frozenset()),
specifier=None,
marker=None,
)
"""
Expand Down Expand Up @@ -574,7 +598,9 @@ def get_lazy_requirement(requirement: AnyRequirement) -> LazyRequirement:
distribution=requirement.name,
url=requirement.url,
extras=frozenset(requirement.extras),
specifier=get_lazy_specifier_set(requirement.specifier),
specifier=get_lazy_specifier_set(requirement.specifier)
if requirement.specifier
else None,
marker=requirement.marker,
)
if isinstance(requirement, LazyRequirement):
Expand Down Expand Up @@ -626,7 +652,12 @@ def compose(lhs: AnyRequirement, rhs: AnyRequirement) -> LazySpecifierSet | Lazy
distribution = lhr.distribution or rhr.distribution
url = lhr.url or rhr.url
extras = frozenset(chain(lhr.extras, rhr.extras))
specifier = compose(lhr.specifier, rhr.specifier)
if lhr.specifier is None:
specifier = rhr.specifier
elif rhr.specifier is None:
specifier = lhr.specifier
else:
specifier = compose(lhr.specifier, rhr.specifier)
marker: Marker | None
if lhr.marker is None:
marker = rhr.marker
Expand All @@ -646,7 +677,18 @@ def compose(lhs: AnyRequirement, rhs: AnyRequirement) -> LazySpecifierSet | Lazy
else:
lhss = get_lazy_specifier_set(lhs)
rhss = get_lazy_specifier_set(rhs)
return LazySpecifierSet(frozenset(chain(lhss.specifiers, rhss.specifiers)))
if isinstance(lhss, EagerLazySpecifierSet) and isinstance(rhss, EagerLazySpecifierSet):
return EagerLazySpecifierSet(frozenset(chain(lhss.specifiers, rhss.specifiers)))
specifiers: list[LazySpecifierSet] = []
if isinstance(lhss, CompositeLazySpecifierSet):
specifiers.extend(lhss.specifiers)
else:
specifiers.append(lhss)
if isinstance(rhss, CompositeLazySpecifierSet):
specifiers.extend(rhss.specifiers)
else:
specifiers.append(rhss)
return CompositeLazySpecifierSet(frozenset(specifiers))


class LazyRequirementSet(ABC):
Expand Down
26 changes: 26 additions & 0 deletions compreq/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,32 @@ def devreleases(
return AllLazyReleaseSet(distribution)


@dataclass(order=True, frozen=True)
class DefaultPythonLazyVersion(LazyVersion):
"""Which version of Python to use while computing requirements."""

async def resolve(self, context: DistributionContext) -> Version:
return context.default_python


def default_python() -> LazyVersion:
"""Which version of Python to use while computing requirements."""
return DefaultPythonLazyVersion()


@dataclass(order=True, frozen=True)
class PythonSpecifierLazySpecifierSet(LazySpecifierSet):
"""Which versions of Python are allowed."""

async def resolve(self, context: DistributionContext) -> SpecifierSet:
return context.python_specifier


def python_specifier() -> LazySpecifierSet:
"""Which versions of Python are allowed."""
return PythonSpecifierLazySpecifierSet()


@dataclass(order=True, frozen=True)
class MinLazyRelease(LazyRelease):
"""
Expand Down
8 changes: 0 additions & 8 deletions compreq/roots.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,6 @@ def for_python(
)
)

@property
def default_python(self) -> Version:
return self._context.default_python

@property
def python_specifier(self) -> SpecifierSet:
return self._context.python_specifier

def resolve_release(self, distribution: str, release: AnyRelease) -> Release:
context = self._context.for_distribution(distribution)
future = get_lazy_release(release).resolve(context)
Expand Down
6 changes: 3 additions & 3 deletions requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def set_python_version_in_github_actions(comp_req: cr.CompReq) -> None:
python_release_set = comp_req.resolve_release_set("python", comp_req.python_specifier)
python_release_set = comp_req.resolve_release_set("python", cr.python_specifier())
minor_versions = sorted(
set(cr.floor(cr.MINOR, r.version, keep_trailing_zeros=False) for r in python_release_set)
)
Expand All @@ -26,7 +26,7 @@ def set_python_version(comp_req: cr.CompReq, pyproject: cr.PoetryPyprojectFile)

pyproject.set_python_classifiers(comp_req)
set_python_version_in_github_actions(comp_req)
version = comp_req.default_python
version = comp_req.resolve_version("python", cr.default_python())

tool = pyproject.toml["tool"]
tool["isort"]["py_version"] = int(f"{version.major}{version.minor}")
Expand Down Expand Up @@ -65,7 +65,7 @@ def main() -> None:
cr.dist("beautifulsoup4") & default_range,
cr.dist("packaging") & default_range,
cr.dist("pip") & default_range,
cr.dist("python") & comp_req.python_specifier,
cr.dist("python") & cr.python_specifier(),
cr.dist("python-dateutil") & default_range,
cr.dist("requests") & default_range,
cr.dist("tomlkit") & default_range,
Expand Down
Loading

0 comments on commit 6423140

Please sign in to comment.