Lock discrepancy

class wreck.lock_discrepancy.PkgsWithIssues
wreck.lock_discrepancy.PkgsWithIssues: dict[str, dict[str, packaging.version.Version | set[packaging.version.Version]]]

Packages by a dict containing highest version and other versions

wreck.lock_discrepancy.__all__: tuple[str, str, str, str, str, str, str, str, str] = ("PkgsWithIssues", "Resolvable", "ResolvedMsg", "UnResolvable",    "get_ss_set", "filter_acceptable", "has_discrepancies_version",    "get_the_fixes", "write_to_file_nudge_pin")

Module exports

wreck.lock_discrepancy.is_module_debug: bool = False

Flag to turn on module level logging. Should be off in production

wreck.lock_discrepancy._logger: logging.Logger

Module level logger

class wreck.lock_discrepancy.Resolvable(venv_path: str | Path, pkg_name: str, qualifiers: str, nudge_unlock: str, nudge_lock: str)

Resolvable dependency conflict. Can find the lines for the pkg, in .unlock and .lock files, using (loader and) venv_path and pkg_name.

Limitation: Qualifiers e.g. python_version and os_name

  • haphazard usage

All pkg lines need the same qualifier. Often missing. Make uniform. Like a pair of earings.

  • rigorous usage

There can be one or more qualifiers. In which case, nonobvious which qualifier to use where.

Variables:
  • venv_path (str | pathlib.Path) – Relative or absolute path to venv base folder

  • pkg_name (str) – package name

  • qualifiers (str) – qualifiers joined together into one str. Whitespace before the 1st semicolon not preserved.

  • nudge_unlock (str) – For .unlock files. Nudge pin e.g. pkg_name>=some_version. If pkg_name entry in an .unlock file, replace otherwise add entry

  • nudge_lock (str) – For .lock files. Nudge pin e.g. pkg_name==some_version. If pkg_name entry in a .lock file, replace otherwise add entry

nudge_lock: str
nudge_unlock: str
pkg_name: str
qualifiers: str
venv_path: str | Path
class wreck.lock_discrepancy.ResolvedMsg(venv_path: str, abspath_f: Path, nudge_pin_line: str)

Fixed dependency version discrepancies (aka issues)

Does not include the original line

Variables:
  • venv_path (str) – venv relative or absolute path

  • abspath_f (pathlib.Path) – Absolute path to requirements file

  • nudge_pin_line (str) – What the line will become

abspath_f: Path
nudge_pin_line: str
venv_path: str
class wreck.lock_discrepancy.UnResolvable(venv_path: str, pkg_name: str, qualifiers: str, sss: set[SpecifierSet], v_highest: Version, v_others: set[Version], pins: set[PinDatum])

Cannot resolve this dependency conflict.

Go out of our way to clearly and cleanly present sufficient details on the issue.

The most prominent details being the package name and Pins (from relevant .unlock files).

Track down issue

With issue explanation. Look at the .lock to see the affected package’s parent(s). The parents’ package pins may be the cause of the conflict.

The parents’ package pyproject.toml file is the first place to look for strange dependency restrictions. Why a restriction was imposed upon a dependency may not be well documented. Look in the repo issues. Search for the dependency package name

Upgrading

lock inspect is not a dependency upgrader. Priority is to sync .unlock and .lock files.

Recommend always doing a dry run pip compile --dry-run some-requirement.in or looking at upgradable packages within the venv. pip list -o

Variables:
  • venv_path (str | pathlib.Path) – Relative or absolute path to venv base folder

  • pkg_name (str) – package name

  • qualifiers (str) – qualifiers joined together into one str. Whitespace before the 1st semicolon not preserved.

  • sss (set[packaging.specifiers.SpecifierSet]) – Set of SpecifierSet, for this package, are the dependency version restrictions found in .unlock files

  • v_highest (packaging.version.Version) – Hints at the process taken to find a Version which satisfies SpecifierSets. First this highest version was checked

  • v_others (set[packaging.version.Version]) – After highest version, all other potential versions are checked. The potential versions come from the .lock files. So if a version doesn’t exist in one .lock, it’s never tried.

  • pins (set[wreck.lock_datum.PinDatum]) –

    Has the absolute path to each requirements file and the dependency version restriction.

    Make this readable

pins: set[PinDatum]
pkg_name: str
qualifiers: str
sss: set[SpecifierSet]
v_highest: Version
v_others: set[Version]
venv_path: str
wreck.lock_discrepancy.extract_full_package_name(line, pkg_name_desired)

Extract first occurrence of exact package name. Algo uses tokens, not regex.

Parameters:
  • line (str) – .unlock or .lock file line. Has package name, but might not be exact

  • pkg_name_desired (str) – pkg name would like an exact match

Returns:

is_different_pkg – True indicates can be either similar or different package is_known_oper – None means could not parse line. bool known/unknown operator pkg – package name. Alphanumeric hyphen underscore and period oper - operator e.g. >= remaining – everything following the operator

Return type:

tuple[bool, bool | None, str | None, str | None, str | None]

wreck.lock_discrepancy.filter_acceptable(set_pindatum, set_ss, highest, others)

SpecifierSet does the heavy lifting, filtering out unacceptable possibilities

get_ss_set() has already filtered invalid operators

Parameters:
Returns:

set_acceptable, lst_specifiers, is_eq_affinity

Return type:

tuple[set[packaging.specifiers.SpecifierSet], list[list[str]], packaging.version.Version | None]

wreck.lock_discrepancy.get_compatible_release(highest: Version, lsts_specifiers: list[list[str]])
highest    = <Version('25.3')>
lst_specifiers = ['~=25.0']
lsts_specifiers = [['<=25.3'], ['~=25.0']]
set_acceptable = {<Version('25.0')>, <Version('25.3')>}

For nudge pin lock

Since ~=25.0 is equivalent to >= 25.0, == 25.* Any other limiter would be on the upper limit Version

highest acceptable Version is correct

For nudge pin unlock

In the unlock nudge pin, would be nice to take into account all lsts_specifiers. Create a set. Combine all and comma separate

Parameters:
Returns:

lock and unlock nudge pins and True indicates resolvable

Return type:

tuple[str, str, bool]

wreck.lock_discrepancy.get_ss_set(set_pindatum)

Create a set of all SpecifierSet

Parameters:

set_pindatum (set[wreck.lock_datum.PinDatum]) – PinDatum for the same package, from all .lock files

Returns:

set of SpecifierSet

Return type:

set[packaging.specifiers.SpecifierSet]

Raises:
wreck.lock_discrepancy.get_the_fixes(set_acceptable, lsts_specifiers, highest, is_eq_affinity_value, is_ss_count_zero)

When a .lock file is created, it is built from:

  • one .in file

  • recursively resolved constraints and requirements files

But not all. Therein lies the rub. Trying to choose based on the limited info at hand.

This algo will fail when there is an unnoticed pin that limits the version.

get_ss_set() has already filtered invalid operators

Parameters:
Returns:

lock nudge pin w/o preceding pkg_name unlock nudge pin w/o preceding pkg_name bool False if unresolvable otherwise True

Return type:

tuple[str | None, str | None, bool]

Raises:
wreck.lock_discrepancy.has_discrepancies_version(d_by_pkg: wreck.lock_datum.DatumByPkg)

Across .lock files, packages with discrepancies.

Comparison limited to equality

Parameters:

d_by_pkg (wreck.lock_datum.DatumByPkg) – Within one venv, all lock packages’ set[PinDatum]

Returns:

pkg name / highest version. Only packages with discrepancies. With the highest version, know which version to nudge to.

Return type:

wreck.lock_discrepancy.PkgsWithIssues

wreck.lock_discrepancy.nudge_pin_lock_v1(found)

Assumes found is not None aka unresolvable

Will later need to prepend pkg_name

Parameters:

str_operator (packaging.version.Version) – Semantic version

Returns:

lock nudge pin

Return type:

str

wreck.lock_discrepancy.nudge_pin_unlock_v1(str_operator, found)

Assumes found is not None aka unresolvable.

Will later need to prepend pkg_name

Parameters:
Returns:

unlock nudge pin

Return type:

str

wreck.lock_discrepancy.write_to_file_nudge_pin(path_f, pkg_name, nudge_pin_line)

Nudge pin must include a newline (os.linesep) If package line exists in file, overwrite. Otherwise append nudge pin line

Parameters:
  • path_f (pathlib.Path) – Absolute path to either a .unlock or .lock file. Only a comment if no preceding whitespace

  • pkg_name (str) – Package name. Should be lowercase

  • nudge_pin_line (str) – Format [package name][operator][version][qualifiers][os.linesep]