diff --git a/.gitignore b/.gitignore index ee05595..4054519 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,9 @@ -.idea -__pycache__ +result +result-* + +__pycache__/ +*.py[cod] +*.egg-info/ +build/ +dist/ +.venv/ diff --git a/eos/utils/version.py b/eos/utils/version.py index 5c6b5da..a3deeba 100644 --- a/eos/utils/version.py +++ b/eos/utils/version.py @@ -1,31 +1,53 @@ """ Versions helper. -Wrapper on distutils.version to allow more laziness. +Small version wrapper used for Symfony version comparisons. """ -from distutils.version import LooseVersion +from __future__ import annotations +import re +from functools import total_ordering -class Version(LooseVersion): + +@total_ordering +class Version: """ EOS Version. - Simple wrapper on distutils.LooseVersion to provide more abstraction on version comparison. + distutils was removed from the stdlib in Python 3.12+, so we keep a tiny + implementation that supports the comparisons EOS needs (e.g. "4.1" <= "4.4"). """ - def _cmp(self, other): - """ - Comparison override to support extra types. - - If other is not an Version instance (or LooseVersion by inheritance), cast it to string and Version. - This provides integers support among others. - The base method is then called. - - :param other: the other object to compare against - """ - - if not isinstance(other, Version): - other = Version(str(other)) - - return super()._cmp(other) + def __init__(self, value): + self.raw = str(value) + + # Extract the first dotted numeric version-like substring. + # Examples: + # - "5.0.1" -> (5, 0, 1) + # - "Symfony 5.0.1" -> (5, 0, 1) + # - "5.0.1-rc1" -> (5, 0, 1) + match = re.search(r"\d+(?:\.\d+)*", self.raw) + if match: + self.parts = tuple(int(p) for p in match.group(0).split(".")) + else: + self.parts = (0,) + + def _cmp_tuple(self, other: object) -> tuple[tuple[int, ...], tuple[int, ...]]: + other_version = other if isinstance(other, Version) else Version(other) + + max_len = max(len(self.parts), len(other_version.parts)) + left = self.parts + (0,) * (max_len - len(self.parts)) + right = other_version.parts + (0,) * (max_len - len(other_version.parts)) + return left, right + + def __eq__(self, other: object) -> bool: + left, right = self._cmp_tuple(other) + return left == right + + def __lt__(self, other: object) -> bool: + left, right = self._cmp_tuple(other) + return left < right + + def __str__(self) -> str: + return self.raw diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..43f833f --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1771008912, + "narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a82ccc39b39b621151d6732718e3e250109076fa", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..2125920 --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +{ + description = "eos - Enemies Of Symfony"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + eos = pkgs.callPackage ./package.nix { }; + in + { + packages = { + default = eos; + eos = eos; + }; + + apps.default = flake-utils.lib.mkApp { drv = eos; }; + + devShells.default = import ./shell.nix { inherit pkgs; }; + }); +} + diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..fb2dd7a --- /dev/null +++ b/package.nix @@ -0,0 +1,41 @@ +{ lib +, python3Packages +}: + +python3Packages.buildPythonApplication rec { + pname = "eos"; + version = "1.0.0"; + + src = lib.cleanSource ./.; + + # setup.py (setuptools), no pyproject.toml + pyproject = false; + + nativeBuildInputs = with python3Packages; [ + setuptoolsBuildHook + pypaInstallHook + # setup.py imports `eos`, which imports `requests` (see eos.Dockerfile). + requests + beautifulsoup4 + lxml + defusedxml + ]; + + propagatedBuildInputs = with python3Packages; [ + requests + beautifulsoup4 + lxml + defusedxml + ]; + + pythonImportsCheck = [ + "eos" + ]; + + meta = with lib; { + description = "Enemies Of Symfony"; + homepage = "https://github.com/synacktiv/eos"; + license = licenses.gpl3Only; + mainProgram = "eos"; + }; +} diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..7728e85 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +{ pkgs ? import { } }: + +let + eos = pkgs.callPackage ./package.nix { }; +in +pkgs.mkShell { + packages = [ + eos + ]; +} +