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

Drag and drop source code and tests for poetry-semver #45

Merged
merged 6 commits into from
Apr 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
506 changes: 250 additions & 256 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ python = ">=3.7,<4.0"
astroid = "^2.0"
packaging = ">=21.3"
toml = "^0.10.2"
poetry-semver = "^0.1.0"

[tool.poetry.dev-dependencies]
tox = "^3.24.5"
Expand Down
3 changes: 2 additions & 1 deletion requirements_detector/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
from typing import List, Union

import toml
from semver import parse_constraint

from .exceptions import CouldNotParseRequirements, RequirementsNotFound
from .handle_setup import from_setup_py
from .requirement import DetectedRequirement
from .poetry_semver import parse_constraint


__all__ = [
"find_requirements",
Expand Down
8 changes: 8 additions & 0 deletions requirements_detector/poetry_semver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
poetry-semver
=============

This is a carbon-copy of the original, and now archived, poetry-semver
package originally located at https://github.com/python-poetry/semver

It functionality is still needed, so a drag and drop here was done to ensure
portability.
160 changes: 160 additions & 0 deletions requirements_detector/poetry_semver/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import re

from .empty_constraint import EmptyConstraint
from .patterns import (
BASIC_CONSTRAINT,
CARET_CONSTRAINT,
TILDE_CONSTRAINT,
TILDE_PEP440_CONSTRAINT,
X_CONSTRAINT,
)
from .version import Version
from .version_constraint import VersionConstraint
from .version_range import VersionRange
from .version_union import VersionUnion

__version__ = "0.1.0"


def parse_constraint(constraints): # type: (str) -> VersionConstraint
if constraints == "*":
return VersionRange()

or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip())
or_groups = []
for constraints in or_constraints:
and_constraints = re.split("(?<!^)(?<![=>< ,]) *(?<!-)[, ](?!-) *(?!,|$)", constraints)
constraint_objects = []

if len(and_constraints) > 1:
for constraint in and_constraints:
constraint_objects.append(parse_single_constraint(constraint))
else:
constraint_objects.append(parse_single_constraint(and_constraints[0]))

if len(constraint_objects) == 1:
constraint = constraint_objects[0]
else:
constraint = constraint_objects[0]
for next_constraint in constraint_objects[1:]:
constraint = constraint.intersect(next_constraint)

or_groups.append(constraint)

if len(or_groups) == 1:
return or_groups[0]
else:
return VersionUnion.of(*or_groups)


def parse_single_constraint(constraint): # type: (str) -> VersionConstraint
m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint)
if m:
return VersionRange()

# Tilde range
m = TILDE_CONSTRAINT.match(constraint)
if m:
version = Version.parse(m.group(1))

high = version.stable.next_minor
if len(m.group(1).split(".")) == 1:
high = version.stable.next_major

return VersionRange(version, high, include_min=True, always_include_max_prerelease=True)

# PEP 440 Tilde range (~=)
m = TILDE_PEP440_CONSTRAINT.match(constraint)
if m:
precision = 1
if m.group(3):
precision += 1

if m.group(4):
precision += 1

version = Version.parse(m.group(1))

if precision == 2:
low = version
high = version.stable.next_major
else:
low = Version(version.major, version.minor, version.patch)
high = version.stable.next_minor

return VersionRange(low, high, include_min=True, always_include_max_prerelease=True)

# Caret range
m = CARET_CONSTRAINT.match(constraint)
if m:
version = Version.parse(m.group(1))

return VersionRange(
version,
version.next_breaking,
include_min=True,
always_include_max_prerelease=True,
)

# X Range
m = X_CONSTRAINT.match(constraint)
if m:
op = m.group(1)
major = int(m.group(2))
minor = m.group(3)

if minor is not None:
version = Version(major, int(minor), 0)

result = VersionRange(
version,
version.next_minor,
include_min=True,
always_include_max_prerelease=True,
)
else:
if major == 0:
result = VersionRange(max=Version(1, 0, 0))
else:
version = Version(major, 0, 0)

result = VersionRange(
version,
version.next_major,
include_min=True,
always_include_max_prerelease=True,
)

if op == "!=":
result = VersionRange().difference(result)

return result

# Basic comparator
m = BASIC_CONSTRAINT.match(constraint)
if m:
op = m.group(1)
version = m.group(2)

if version == "dev":
version = "0.0-dev"

try:
version = Version.parse(version)
except ValueError:
raise ValueError("Could not parse version constraint: {}".format(constraint))

if op == "<":
return VersionRange(max=version)
elif op == "<=":
return VersionRange(max=version, include_max=True)
elif op == ">":
return VersionRange(min=version)
elif op == ">=":
return VersionRange(min=version, include_min=True)
elif op == "!=":
return VersionUnion(VersionRange(max=version), VersionRange(min=version))
else:
return version

raise ValueError("Could not parse version constraint: {}".format(constraint))
30 changes: 30 additions & 0 deletions requirements_detector/poetry_semver/empty_constraint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from .version_constraint import VersionConstraint


class EmptyConstraint(VersionConstraint):
def is_empty(self):
return True

def is_any(self):
return False

def allows(self, version):
return False

def allows_all(self, other):
return other.is_empty()

def allows_any(self, other):
return False

def intersect(self, other):
return self

def union(self, other):
return other

def difference(self, other):
return self

def __str__(self):
return "<empty>"
2 changes: 2 additions & 0 deletions requirements_detector/poetry_semver/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ParseVersionError(ValueError):
pass
17 changes: 17 additions & 0 deletions requirements_detector/poetry_semver/patterns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import re

MODIFIERS = (
"[._-]?"
r"((?!post)(?:beta|b|c|pre|RC|alpha|a|patch|pl|p|dev)(?:(?:[.-]?\d+)*)?)?"
r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?"
)

_COMPLETE_VERSION = r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS)

COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION)

CARET_CONSTRAINT = re.compile(r"(?i)^\^({})$".format(_COMPLETE_VERSION))
TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION))
TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION))
X_CONSTRAINT = re.compile(r"^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$")
BASIC_CONSTRAINT = re.compile(r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION))
Loading