[ADD] poetry: pin a dependency per architecture
factur-x >= 4.0 needs saxonche, which Saxonica ships no s390x wheel for. Pin 3.x there, 4.2+ everywhere else. PEP 508 markers now survive the whole chain: kept instead of evaluated, and each line reaches "poetry add" as a single argument -- the "grep -v ';'" silently dropped them. Since "poetry add" dedupes by name, the multi-constraint form, which the CLI cannot express, is written back into the pyproject and the lock regenerated. PyMuPDF is set aside on s390x for the same reason: MuPDF does not build there. Along the way, the ignore list missed pymssql, whose key carried its version, and two pre-existing solver deadlocks, lxml-html-clean and s3fs, blocked any regeneration. --- FR --- factur-x >= 4.0 dépend de saxonche, sans roue s390x chez Saxonica. On épingle la 3.x là-bas, la 4.2+ ailleurs. Le marqueur PEP 508 traverse maintenant la chaîne : il est conservé au lieu d'être évalué, et chaque ligne devient un seul argument de « poetry add » — le « grep -v ';' » écartait ces lignes en silence. « poetry add » dédupliquant par nom, la forme multi-contraintes, hors de portée du CLI, est réécrite dans le pyproject, puis le lock refait. PyMuPDF est écarté sur s390x pour la même raison : MuPDF ne s'y construit pas. Au passage, la liste d'ignorés ratait pymssql, dont la clé portait sa version, et deux blocages préexistants du solveur, lxml-html-clean et s3fs, interdisaient toute régénération. Assisted-by: Claude Opus 5
This commit is contained in:
parent
b939aafc03
commit
3fbde848f8
5 changed files with 1954 additions and 948 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -21,11 +21,12 @@ authors = [ "Mathieu Benoit <mathben@technolibre.ca>",]
|
||||||
python = ">=3.12.10,<3.13"
|
python = ">=3.12.10,<3.13"
|
||||||
a2wsgi = ">=1.10.6"
|
a2wsgi = ">=1.10.6"
|
||||||
aiobotocore = "3.1.2"
|
aiobotocore = "3.1.2"
|
||||||
|
altcha = ">=2.0.0"
|
||||||
ansi2html = "^1.9.2"
|
ansi2html = "^1.9.2"
|
||||||
apispec = "^6.10.0"
|
apispec = "^6.10.0"
|
||||||
asn1crypto = "1.5.1"
|
asn1crypto = "1.5.1"
|
||||||
astor = "^0.8.1"
|
astor = "^0.8.1"
|
||||||
avalara = "^26.4.0"
|
avalara = "^26.7.3"
|
||||||
babel = "2.10.3"
|
babel = "2.10.3"
|
||||||
bandit = "^1.9.4"
|
bandit = "^1.9.4"
|
||||||
base64io = "^1.0.3"
|
base64io = "^1.0.3"
|
||||||
|
|
@ -33,7 +34,7 @@ beautifulsoup4 = "4.13.5"
|
||||||
bokeh = "3.6.3"
|
bokeh = "3.6.3"
|
||||||
boto3 = "1.42.42"
|
boto3 = "1.42.42"
|
||||||
botocore = "1.42.42"
|
botocore = "1.42.42"
|
||||||
cachetools = "^7.1.1"
|
cachetools = "^7.1.7"
|
||||||
cairosvg = "^2.9.0"
|
cairosvg = "^2.9.0"
|
||||||
caldav = "1.3.9"
|
caldav = "1.3.9"
|
||||||
cbor2 = "5.6.2"
|
cbor2 = "5.6.2"
|
||||||
|
|
@ -43,21 +44,20 @@ code-writer = "^1.3.0"
|
||||||
colorama = "^0.4.6"
|
colorama = "^0.4.6"
|
||||||
contextvars = "^2.4"
|
contextvars = "^2.4"
|
||||||
cryptography = "46.0.5"
|
cryptography = "46.0.5"
|
||||||
cssselect = "^1.4.0"
|
cssselect = "^1.5.0"
|
||||||
cython = "^3.2.4"
|
cython = "^3.2.9"
|
||||||
dateparser = "^1.4.0"
|
dateparser = "^1.4.2"
|
||||||
decorator = "5.1.1"
|
decorator = "5.1.1"
|
||||||
dnspython = "^2.8.0"
|
dnspython = "^2.8.0"
|
||||||
docutils = "0.20.1"
|
docutils = "0.20.1"
|
||||||
dropbox = "^12.0.2"
|
dropbox = "^12.2.1"
|
||||||
easypost = "7.15.0"
|
easypost = "7.15.0"
|
||||||
email-validator = "^2.3.0"
|
email-validator = "^2.3.0"
|
||||||
extendable = ">=0.0.4"
|
extendable = ">=0.0.4"
|
||||||
extendable-pydantic = ">=1.2.0"
|
extendable-pydantic = ">=1.2.0"
|
||||||
extract-msg = "^0.55.0"
|
extract-msg = "^0.56.0"
|
||||||
extruct = "^0.18.0"
|
extruct = "^0.18.0"
|
||||||
ezdxf = "^1.4.3"
|
ezdxf = "^1.4.4"
|
||||||
factur-x = "^4.2"
|
|
||||||
fastapi = ">=0.110.0"
|
fastapi = ">=0.110.0"
|
||||||
filetype = "^1.2.0"
|
filetype = "^1.2.0"
|
||||||
flake8 = "^7.3.0"
|
flake8 = "^7.3.0"
|
||||||
|
|
@ -65,9 +65,9 @@ formio-data = "^2.1.7"
|
||||||
freezegun = "1.2.1"
|
freezegun = "1.2.1"
|
||||||
geocoder = "^1.38.1"
|
geocoder = "^1.38.1"
|
||||||
geoip2 = "2.9.0"
|
geoip2 = "2.9.0"
|
||||||
geojson = "^3.2.0"
|
geojson = "^3.3.0"
|
||||||
gevent = "24.2.1"
|
gevent = "24.2.1"
|
||||||
gitpython = "^3.1.50"
|
gitpython = "^3.1.59"
|
||||||
giturlparse = "0.12.0"
|
giturlparse = "0.12.0"
|
||||||
googletrans-py = "4.0.0"
|
googletrans-py = "4.0.0"
|
||||||
greenlet = "3.0.3"
|
greenlet = "3.0.3"
|
||||||
|
|
@ -76,36 +76,38 @@ icalendar = "5.0.13"
|
||||||
idna = "3.6"
|
idna = "3.6"
|
||||||
img2pdf = "^0.6.3"
|
img2pdf = "^0.6.3"
|
||||||
isort = "7.0.0"
|
isort = "7.0.0"
|
||||||
|
itsdangerous = "^2.2.0"
|
||||||
jinja2 = "3.1.2"
|
jinja2 = "3.1.2"
|
||||||
js2py = "^0.74"
|
js2py = "^0.74"
|
||||||
libsass = "0.22.0"
|
libsass = "0.22.0"
|
||||||
lottie = "^0.7.2"
|
lottie = "^0.7.2"
|
||||||
lxml = "5.2.1"
|
lxml = "5.2.1"
|
||||||
lxml-html-clean = "^0.4.4"
|
lxml-html-clean = "<0.4.5"
|
||||||
mako = "^1.3.12"
|
mako = "^1.4.1"
|
||||||
markdown2 = "2.5.1"
|
markdown2 = "2.5.1"
|
||||||
markdownify = "0.13.1"
|
markdownify = "0.13.1"
|
||||||
markupsafe = "2.1.5"
|
markupsafe = "2.1.5"
|
||||||
matplotlib = "^3.10.9"
|
matplotlib = "^3.11.1"
|
||||||
meteostat = "1.6.8"
|
meteostat = "1.6.8"
|
||||||
mock = "^5.2.0"
|
mock = "^5.2.0"
|
||||||
mpld3 = "0.5.10"
|
mpld3 = "0.5.10"
|
||||||
msgraphfs = "^0.4"
|
msgraphfs = "^0.5"
|
||||||
mysqlclient = "^2.2.8"
|
mysqlclient = "^2.2.8"
|
||||||
nextcloud-api-wrapper = "^0.2.3"
|
nextcloud-api-wrapper = "^0.2.3"
|
||||||
num2words = "0.5.13"
|
num2words = "0.5.13"
|
||||||
numpy = ">=1.15"
|
numpy = ">=1.15"
|
||||||
numpy-financial = "<=1.0.0"
|
numpy-financial = "<=1.0.0"
|
||||||
numpy-stl = "^3.2.0"
|
numpy-stl = "^4.0.0"
|
||||||
oauthlib = "^3.3.1"
|
oauthlib = "^3.3.1"
|
||||||
|
odoo-test-helper = "^2.1.3"
|
||||||
odoorpc = "^0.10.1"
|
odoorpc = "^0.10.1"
|
||||||
ofxparse = "0.21"
|
ofxparse = "0.21"
|
||||||
ollama = "^0.6.2"
|
ollama = "^0.6.2"
|
||||||
openai = "^2.34.0"
|
openai = "^2.53.0"
|
||||||
openpyxl = "3.1.2"
|
openpyxl = "3.1.2"
|
||||||
openupgradelib = "^3.13.1"
|
openupgradelib = "^3.13.6"
|
||||||
openwebui-client = ">=0.3.0"
|
openwebui-client = ">=0.3.0"
|
||||||
orjson = "^3.11.8"
|
orjson = "^3.11.9"
|
||||||
ovh = "^1.2.0"
|
ovh = "^1.2.0"
|
||||||
pandas = "2.3.3"
|
pandas = "2.3.3"
|
||||||
paramiko = "<4.0.0"
|
paramiko = "<4.0.0"
|
||||||
|
|
@ -115,14 +117,14 @@ pathspec = "^1.1.1"
|
||||||
pdf2image = "^1.17.0"
|
pdf2image = "^1.17.0"
|
||||||
pdfminer-six = "20260107"
|
pdfminer-six = "20260107"
|
||||||
pexpect = "^4.9.0"
|
pexpect = "^4.9.0"
|
||||||
phonenumbers = "^9.0.29"
|
phonenumbers = "^9.0.36"
|
||||||
pillow = "12.2.0"
|
pillow = "12.2.0"
|
||||||
plaid-python = "^39.2.0"
|
plaid-python = "^42.0.0"
|
||||||
plotly = "^6.7.0"
|
plotly = "^6.9.0"
|
||||||
polib = "1.1.1"
|
polib = "1.1.1"
|
||||||
pre-commit = "^4.6.0"
|
pre-commit = "^4.6.2"
|
||||||
premailer = "^3.10.0"
|
premailer = "^3.10.0"
|
||||||
prometheus-client = "^0.25.0"
|
prometheus-client = "^0.26.0"
|
||||||
psutil = "5.9.8"
|
psutil = "5.9.8"
|
||||||
psycopg2 = "2.9.9"
|
psycopg2 = "2.9.9"
|
||||||
py3dns = "^4.0.2"
|
py3dns = "^4.0.2"
|
||||||
|
|
@ -135,10 +137,9 @@ pydevd-odoo = "^1.2.2"
|
||||||
pyftpdlib = "^2.2.0"
|
pyftpdlib = "^2.2.0"
|
||||||
pygithub = "^2.9.1"
|
pygithub = "^2.9.1"
|
||||||
pyjsparser = "^2.7.1"
|
pyjsparser = "^2.7.1"
|
||||||
pyjwt = "^2.12.1"
|
pyjwt = "^2.13.0"
|
||||||
pylint = "^4.0.5"
|
pylint = "^4.0.7"
|
||||||
pylint-odoo = "^10.0.2"
|
pylint-odoo = "^10.0.9"
|
||||||
pymupdf = "^1.27.2.3"
|
|
||||||
pyncclient = "^0.7"
|
pyncclient = "^0.7"
|
||||||
pyopenssl = "26.0.0"
|
pyopenssl = "26.0.0"
|
||||||
pypdf = ">=3.1.0"
|
pypdf = ">=3.1.0"
|
||||||
|
|
@ -154,13 +155,13 @@ python-dotenv = ">=1.0.0"
|
||||||
python-jose = "^3.5.0"
|
python-jose = "^3.5.0"
|
||||||
python-json-logger = "^4.1.0"
|
python-json-logger = "^4.1.0"
|
||||||
python-ldap = "3.4.4"
|
python-ldap = "3.4.4"
|
||||||
python-multipart = "^0.0.27"
|
python-multipart = "^0.0.32"
|
||||||
python-pptx = "^1.0.2"
|
python-pptx = "^1.0.2"
|
||||||
python-resize-image = "^1.1.20"
|
python-resize-image = "^1.1.20"
|
||||||
python-slugify = "^8.0.4"
|
python-slugify = "^8.0.4"
|
||||||
python-stdnum = "1.19"
|
python-stdnum = "1.19"
|
||||||
python-telegram-bot = "^22.7"
|
python-telegram-bot = "^22.8"
|
||||||
pytz = "^2026.2"
|
pytz = "^2026.3.post1"
|
||||||
pyusb = "1.2.1"
|
pyusb = "1.2.1"
|
||||||
pyyaml = "^6.0.3"
|
pyyaml = "^6.0.3"
|
||||||
pyzbar = "^0.1.9"
|
pyzbar = "^0.1.9"
|
||||||
|
|
@ -168,27 +169,28 @@ pyzk = "^0.9"
|
||||||
qifparse = "^0.5"
|
qifparse = "^0.5"
|
||||||
qrcode = "7.4.2"
|
qrcode = "7.4.2"
|
||||||
radon = "^6.0.1"
|
radon = "^6.0.1"
|
||||||
redis = "^7.4.0"
|
redis = "^8.1.0"
|
||||||
regex = "^2026.4.4"
|
regex = "^2026.7.19"
|
||||||
reportlab = "4.1.0"
|
reportlab = "4.1.0"
|
||||||
requests = ">=2.31.0"
|
requests = "2.31.0"
|
||||||
requests-oauthlib = "^2.0.0"
|
requests-oauthlib = "^2.0.0"
|
||||||
requests-toolbelt = "^1.0.0"
|
requests-toolbelt = "^1.0.0"
|
||||||
responses = "^0.26.0"
|
responses = "^0.26.2"
|
||||||
rjsmin = "1.2.0"
|
rjsmin = "1.2.0"
|
||||||
roulier = "^1.1.1"
|
roulier = "^1.1.1"
|
||||||
s3fs = "2026.4.0"
|
s3fs = "2026.7.0"
|
||||||
schwifty = "2024.4.0"
|
schwifty = "2024.4.0"
|
||||||
sentry-sdk = ">=2.0.0,<=2.22.0"
|
sentry-sdk = ">=2.0.0,<=2.22.0"
|
||||||
setuptools = "80.10.2"
|
setuptools = "80.10.2"
|
||||||
shapely = "^2.1.2"
|
shapely = "^2.1.2"
|
||||||
sqlalchemy = "^2.0.49"
|
sqlalchemy = "^2.0.51"
|
||||||
statsd = "^4.0.1"
|
statsd = "^4.0.1"
|
||||||
tldextract = "^5.3.1"
|
tldextract = "^5.3.2"
|
||||||
tweepy = "^4.16.0"
|
to-3mf = "^0.1.0"
|
||||||
twilio = "^9.10.5"
|
tweepy = "^4.17.0"
|
||||||
typing-extensions = "^4.15.0"
|
twilio = "^9.10.9"
|
||||||
ujson = "^5.12.1"
|
typing-extensions = "^4.16.0"
|
||||||
|
ujson = "^5.13.0"
|
||||||
unidecode = "^1.4.0"
|
unidecode = "^1.4.0"
|
||||||
urllib3 = "2.0.7"
|
urllib3 = "2.0.7"
|
||||||
uvloop = "^0.22.1"
|
uvloop = "^0.22.1"
|
||||||
|
|
@ -204,6 +206,18 @@ xlwt = "1.3.0"
|
||||||
xmltodict = "0.13.0"
|
xmltodict = "0.13.0"
|
||||||
zeep = "4.2.1"
|
zeep = "4.2.1"
|
||||||
|
|
||||||
|
[[tool.poetry.dependencies.pymupdf]]
|
||||||
|
version = "^1.28.2"
|
||||||
|
markers = "platform_machine != \"s390x\""
|
||||||
|
|
||||||
|
[[tool.poetry.dependencies.factur-x]]
|
||||||
|
version = ">=4.2"
|
||||||
|
markers = "platform_machine != \"s390x\""
|
||||||
|
|
||||||
|
[[tool.poetry.dependencies.factur-x]]
|
||||||
|
version = "<4"
|
||||||
|
markers = "platform_machine == \"s390x\""
|
||||||
|
|
||||||
[tool.poetry.dependencies.black]
|
[tool.poetry.dependencies.black]
|
||||||
git = "https://github.com/psf/black.git"
|
git = "https://github.com/psf/black.git"
|
||||||
rev = "24.8.0"
|
rev = "24.8.0"
|
||||||
|
|
@ -214,7 +228,7 @@ rev = "update_imghdr_python3"
|
||||||
|
|
||||||
[tool.poetry.dependencies.fsspec]
|
[tool.poetry.dependencies.fsspec]
|
||||||
extras = [ "s3",]
|
extras = [ "s3",]
|
||||||
version = "^2026.4.0"
|
version = "^2026.7.0"
|
||||||
|
|
||||||
[tool.poetry.dependencies.pysaml2]
|
[tool.poetry.dependencies.pysaml2]
|
||||||
git = "https://github.com/prauscher/pysaml2.git"
|
git = "https://github.com/prauscher/pysaml2.git"
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,26 @@ git+https://github.com/prauscher/pysaml2.git@replace-pyopenssl
|
||||||
|
|
||||||
pandas==2.3.3
|
pandas==2.3.3
|
||||||
pillow==12.2.0
|
pillow==12.2.0
|
||||||
s3fs==2026.4.0
|
# OCA_storage amène « fsspec[s3] » sans borne : l'extra s3 de fsspec 2026.7.0
|
||||||
|
# exige s3fs>=2026.6.0, incompatible avec l'épingle 2026.4.0. On aligne s3fs sur
|
||||||
|
# fsspec ; aiobotocore 3.1.2 reste dans la plage exigée (>=2.19.0,<4.0.0), donc
|
||||||
|
# le trio botocore/boto3 n'a pas à bouger.
|
||||||
|
s3fs==2026.7.0
|
||||||
aiobotocore==3.1.2
|
aiobotocore==3.1.2
|
||||||
botocore==1.42.42
|
botocore==1.42.42
|
||||||
boto3==1.42.42
|
boto3==1.42.42
|
||||||
|
|
||||||
|
# factur-x >= 4.0 dépend de saxonche, qui encapsule SaxonC. Saxonica ne publie
|
||||||
|
# de roue que pour x86_64, aarch64, macOS et Windows — aucune pour s390x, et
|
||||||
|
# aucune distribution source : rien à installer, rien à compiler. La cause est
|
||||||
|
# en amont, GraalVM Native Image ne ciblant pas s390x.
|
||||||
|
# La 3.x n'exige que lxml et pypdf. On y retombe là où saxonche n'existe pas,
|
||||||
|
# au prix de la validation Schematron.
|
||||||
|
factur-x>=4.2 ; platform_machine != 's390x'
|
||||||
|
factur-x<4 ; platform_machine == 's390x'
|
||||||
|
|
||||||
|
# lxml-html-clean 0.4.5 (2026-05-20) exige lxml>=6.1.1, alors qu'Odoo 18 épingle
|
||||||
|
# lxml==5.2.1 : le solveur poetry n'a plus de solution. Les versions jusqu'à
|
||||||
|
# 0.4.4 n'imposent aucune borne sur lxml — vérifié sur PyPI, release par
|
||||||
|
# release. À lever le jour où lxml montera.
|
||||||
|
lxml-html-clean<0.4.5
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,29 @@
|
||||||
Red='\033[0;31m' # Red
|
Red='\033[0;31m' # Red
|
||||||
Color_Off='\033[0m' # Text Reset
|
Color_Off='\033[0m' # Text Reset
|
||||||
|
|
||||||
./.venv.$(cat ".erplibre-version" | xargs)/bin/poetry add -vv $(grep -v ";" ./.venv.$(cat ".erplibre-version" | xargs)/build_dependency.txt | grep -v "*" )
|
VENV="./.venv.$(cat ".erplibre-version" | xargs)"
|
||||||
# poetry add -vv $(grep -v ";" ./.venv/build_dependency.txt | grep -v "*" | sed 's/==/@^/' )
|
BUILD="${VENV}/build_dependency.txt"
|
||||||
# poetry export -f ./.venv/build_dependency.txt --dev | poetry run -- pip install -r /dev/stdin
|
|
||||||
|
# Chaque ligne devient UN argument, jamais plusieurs.
|
||||||
|
#
|
||||||
|
# L'expansion « $(...) » non quotée découpait sur les espaces : une ligne
|
||||||
|
# portant un marqueur — « factur-x>=4.2 ; platform_machine != "s390x" » —
|
||||||
|
# arrivait à poetry en cinq arguments. D'où le « grep -v ';' » historique, qui
|
||||||
|
# écartait purement et simplement ces lignes : la dépendance n'était jamais
|
||||||
|
# ajoutée. Or « poetry add » sait lire une chaîne PEP 508 complète, marqueur
|
||||||
|
# compris — vérifié avec son propre parseur.
|
||||||
|
#
|
||||||
|
# On garde le filtre sur « * » : une contrainte joker n'apporte rien à poetry.
|
||||||
|
mapfile -t DEPS < <(grep -v '\*' "${BUILD}" | grep -v '^[[:space:]]*$')
|
||||||
|
|
||||||
|
if [[ ${#DEPS[@]} -eq 0 ]]; then
|
||||||
|
echo "Aucune dépendance à ajouter depuis ${BUILD}."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${VENV}/bin/poetry" add -vv "${DEPS[@]}"
|
||||||
retVal=$?
|
retVal=$?
|
||||||
if [[ $retVal -ne 0 ]]; then
|
if [[ $retVal -ne 0 ]]; then
|
||||||
echo -e "${Red}Error${Color_Off} ./script/poetry/poetry_add_build_dependency.sh"
|
echo -e "${Red}Error${Color_Off} ./script/poetry/poetry_add_build_dependency.sh"
|
||||||
# cat ./.venv/build_dependency.txt
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,9 @@ def combine_requirements(config):
|
||||||
# remove comments at the end of module
|
# remove comments at the end of module
|
||||||
b = b[: b.index("#")].strip()
|
b = b[: b.index("#")].strip()
|
||||||
comment_depend = ""
|
comment_depend = ""
|
||||||
|
# Défini pour TOUTE ligne : il est relu plus bas, y compris
|
||||||
|
# pour les lignes sans marqueur.
|
||||||
|
keep_marker = ""
|
||||||
if except_sign in b:
|
if except_sign in b:
|
||||||
# current_python_version = platform.python_version()
|
# current_python_version = platform.python_version()
|
||||||
# current_platform = sys.platform
|
# current_platform = sys.platform
|
||||||
|
|
@ -205,6 +208,17 @@ def combine_requirements(config):
|
||||||
config.set_version_python
|
config.set_version_python
|
||||||
)
|
)
|
||||||
req = Requirement(b)
|
req = Requirement(b)
|
||||||
|
# Le nom de l'artefact généré — pyproject.odooXX_pythonYY —
|
||||||
|
# fige la version d'Odoo et celle de Python, PAS
|
||||||
|
# l'architecture. Évaluer « python_version » ici est donc
|
||||||
|
# juste ; évaluer « platform_machine » y graverait
|
||||||
|
# silencieusement l'architecture de la machine qui génère,
|
||||||
|
# et le lock produit sur x86 devient faux sur s390x.
|
||||||
|
# Un tel marqueur est donc CONSERVÉ et transmis à Poetry,
|
||||||
|
# qui sait résoudre les deux branches dans un seul lock.
|
||||||
|
if "platform_machine" in str(req.marker):
|
||||||
|
keep_marker = str(req.marker)
|
||||||
|
else:
|
||||||
is_compatible = req.marker.evaluate(current_platform_2)
|
is_compatible = req.marker.evaluate(current_platform_2)
|
||||||
if not is_compatible:
|
if not is_compatible:
|
||||||
continue
|
continue
|
||||||
|
|
@ -215,6 +229,26 @@ def combine_requirements(config):
|
||||||
# print(comment_depend)
|
# print(comment_depend)
|
||||||
# Rewrite dependancy : module==version
|
# Rewrite dependancy : module==version
|
||||||
b = req.name + str(req.specifier)
|
b = req.name + str(req.specifier)
|
||||||
|
if keep_marker:
|
||||||
|
b += f" ; {keep_marker}"
|
||||||
|
|
||||||
|
# Deux lignes du même module portant des marqueurs DIFFÉRENTS
|
||||||
|
# ne sont pas un conflit : elles s'excluent mutuellement. La
|
||||||
|
# clé inclut donc le marqueur, sans quoi la détection de
|
||||||
|
# versions divergentes les opposerait et en éliminerait une.
|
||||||
|
#
|
||||||
|
# Le nom vient de PEP 508 et non d'un découpage : avec un
|
||||||
|
# marqueur, le premier opérateur de la ligne est souvent celui
|
||||||
|
# du marqueur (« != »), et le découpage rendrait
|
||||||
|
# « factur-x>=4.2 » comme nom de module — que la liste des
|
||||||
|
# ignorés ne retrouverait plus.
|
||||||
|
if keep_marker:
|
||||||
|
module_name = f"{req.name} ; {keep_marker}"
|
||||||
|
dct_requirements[module_name].add(b)
|
||||||
|
dct_requirements_module_filename[b].append(
|
||||||
|
str(requirements_filename)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
# Regroup requirement
|
# Regroup requirement
|
||||||
for sign in lst_sign:
|
for sign in lst_sign:
|
||||||
|
|
@ -258,6 +292,22 @@ def combine_requirements(config):
|
||||||
config, dct_requirements, lst_sign
|
config, dct_requirements, lst_sign
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Un module dont les requirements donnent des variantes par marqueur ne doit
|
||||||
|
# pas recevoir en plus une entrée NUE. Les manifest.py des addons déclarent
|
||||||
|
# « factur-x » sans version ni marqueur : cette entrée s'ajouterait aux deux
|
||||||
|
# branches et poetry recevrait trois contraintes pour le même paquet, dont
|
||||||
|
# une sans condition — ce qui annulerait la distinction par architecture.
|
||||||
|
marked_names = {
|
||||||
|
key.split(";")[0].strip() for key in dct_requirements if ";" in key
|
||||||
|
}
|
||||||
|
for name in marked_names:
|
||||||
|
if name in dct_requirements:
|
||||||
|
del dct_requirements[name]
|
||||||
|
print(
|
||||||
|
f"{Fore.YELLOW}WARNING{Style.RESET_ALL} - '{name}' est déjà"
|
||||||
|
" décliné par marqueur : l'entrée sans condition est ignorée."
|
||||||
|
)
|
||||||
|
|
||||||
# Merge all requirement by insensitive
|
# Merge all requirement by insensitive
|
||||||
dct_requirement_insensitive = {}
|
dct_requirement_insensitive = {}
|
||||||
copy_dct_requirements = dct_requirements.copy()
|
copy_dct_requirements = dct_requirements.copy()
|
||||||
|
|
@ -418,8 +468,13 @@ def combine_requirements(config):
|
||||||
lst_ignore, ignore_requirement_file = get_list_ignored()
|
lst_ignore, ignore_requirement_file = get_list_ignored()
|
||||||
lst_ignored_key = []
|
lst_ignored_key = []
|
||||||
for key in dct_requirements.keys():
|
for key in dct_requirements.keys():
|
||||||
|
# La clé n'est pas toujours un nom nu : selon la branche qui l'a créée
|
||||||
|
# elle porte un marqueur (« paquet ; platform_machine … ») ou une
|
||||||
|
# version (« pymssql<=2.2.5 »). La liste d'ignorés ne nomme que le
|
||||||
|
# module, on ramène donc la clé à son nom avant de comparer.
|
||||||
|
bare_key = re.split(r"[\[<>=!~;]", key)[0].strip()
|
||||||
for ignored in lst_ignore:
|
for ignored in lst_ignore:
|
||||||
if ignored == key.strip():
|
if ignored == bare_key:
|
||||||
lst_ignored_key.append(key)
|
lst_ignored_key.append(key)
|
||||||
for key in lst_ignored_key:
|
for key in lst_ignored_key:
|
||||||
del dct_requirements[key]
|
del dct_requirements[key]
|
||||||
|
|
@ -514,6 +569,68 @@ def get_manifest_external_dependencies(config, dct_requirements, lst_sign):
|
||||||
return dct_requirements
|
return dct_requirements
|
||||||
|
|
||||||
|
|
||||||
|
def apply_marker_variants(pyproject_filename, build_dependency_filename):
|
||||||
|
"""Rétablit les paquets déclinés en plusieurs branches par marqueur.
|
||||||
|
|
||||||
|
« poetry add » déduplique par nom : des deux branches de factur-x, seule la
|
||||||
|
dernière survit, et l'architecture qui n'est pas la sienne hérite de la
|
||||||
|
mauvaise version. Poetry sait pourtant exprimer le cas — mais uniquement
|
||||||
|
dans le pyproject, en liste de contraintes, hors de portée du CLI. On la
|
||||||
|
reconstruit depuis build_dependency.txt, qui porte toutes les branches.
|
||||||
|
|
||||||
|
:return: True si le pyproject a été réécrit, donc si le lock est à refaire
|
||||||
|
"""
|
||||||
|
dct_variant = defaultdict(list)
|
||||||
|
with open(build_dependency_filename, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or ";" not in line:
|
||||||
|
continue
|
||||||
|
req = Requirement(line)
|
||||||
|
dct_variant[req.name].append(
|
||||||
|
{"version": str(req.specifier), "markers": str(req.marker)}
|
||||||
|
)
|
||||||
|
dct_variant = {k: v for k, v in dct_variant.items() if len(v) > 1}
|
||||||
|
if not dct_variant:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(pyproject_filename, "r") as f:
|
||||||
|
dct_pyproject = toml.load(f)
|
||||||
|
dependencies = (
|
||||||
|
dct_pyproject.get("tool", {}).get("poetry", {}).get("dependencies")
|
||||||
|
)
|
||||||
|
if not dependencies:
|
||||||
|
return False
|
||||||
|
|
||||||
|
has_changed = False
|
||||||
|
for name, lst_branch in dct_variant.items():
|
||||||
|
key = next(
|
||||||
|
(k for k in dependencies if k.lower() == name.lower()), None
|
||||||
|
)
|
||||||
|
if not key:
|
||||||
|
continue
|
||||||
|
dependencies[key] = lst_branch
|
||||||
|
has_changed = True
|
||||||
|
str_branch = ", ".join(
|
||||||
|
f"{d['version']} si {d['markers']}" for d in lst_branch
|
||||||
|
)
|
||||||
|
print(f"Décline '{key}' par marqueur : {str_branch}.")
|
||||||
|
|
||||||
|
if has_changed:
|
||||||
|
with open(pyproject_filename, "w") as f:
|
||||||
|
toml.dump(dct_pyproject, f)
|
||||||
|
return has_changed
|
||||||
|
|
||||||
|
|
||||||
|
def call_poetry_lock(config):
|
||||||
|
"""
|
||||||
|
:return: True if success
|
||||||
|
"""
|
||||||
|
venv_dir = f".venv.{config.set_version_erplibre}"
|
||||||
|
status = os.system(f"./{venv_dir}/bin/poetry lock")
|
||||||
|
return status == 0
|
||||||
|
|
||||||
|
|
||||||
def call_poetry_add_build_dependency():
|
def call_poetry_add_build_dependency():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
@ -570,6 +687,15 @@ def main():
|
||||||
status = call_poetry_add_build_dependency()
|
status = call_poetry_add_build_dependency()
|
||||||
if status and pyproject_toml_filename:
|
if status and pyproject_toml_filename:
|
||||||
sorted_dependency_poetry(pyproject_toml_filename)
|
sorted_dependency_poetry(pyproject_toml_filename)
|
||||||
|
build_dependency_filename = (
|
||||||
|
f"./.venv.{config.set_version_erplibre}/build_dependency.txt"
|
||||||
|
)
|
||||||
|
if apply_marker_variants(
|
||||||
|
pyproject_toml_filename, build_dependency_filename
|
||||||
|
):
|
||||||
|
# Le lock écrit par « poetry add » ignore les branches qu'on
|
||||||
|
# vient de rétablir : il ne décrit plus le pyproject.
|
||||||
|
status = call_poetry_lock(config)
|
||||||
if (
|
if (
|
||||||
config.force
|
config.force
|
||||||
and not os.path.islink(poetry_default_lock_path)
|
and not os.path.islink(poetry_default_lock_path)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue