diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 031c944e1..3d0419e35 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -18,10 +18,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.12 - name: Install dependencies run: | @@ -57,10 +57,10 @@ jobs: with: fetch-depth: 0 # fetch all commits and branches for pre-commit - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.12 - name: Set up Node 18 uses: actions/setup-node@v3 @@ -85,31 +85,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install deps and OpenSSL 1.1 - run: | - sudo apt-get -y update - sudo apt-get install -y wget build-essential - wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w - ./config --prefix=/opt/openssl-1.1 --openssldir=/opt/openssl-1.1 - sudo make install - - - name: Download Python and configure python with OpenSSL 1.1 - run: | - cd /usr/local/src - sudo wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz - sudo tar -xzf Python-3.9.0.tgz - cd Python-3.9.0 - sudo ./configure --prefix=/opt/python3.9-openssl1.1 \ - --with-openssl=/opt/openssl-1.1 \ - --enable-optimizations - sudo make -j$(nproc) - sudo make altinstall - - - name: Test OpenSSL version - run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 - name: Install dependencies run: | @@ -119,10 +98,7 @@ jobs: - name: tox run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" - /opt/python3.9-openssl1.1/bin/python3.9 -m venv .venv - source .venv/bin/activate - tox -e py39-unit -- --cov=./ --cov-report=xml + tox -e py312-unit -- --cov=./ --cov-report=xml - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 @@ -138,10 +114,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.12 cache: 'pip' cache-dependency-path: | requirements.txt @@ -172,31 +148,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install deps and OpenSSL 1.1 - run: | - sudo apt-get -y update - sudo apt-get install -y wget build-essential - wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w - ./config --prefix=/opt/openssl-1.1 --openssldir=/opt/openssl-1.1 - sudo make install - - - name: Download Python and configure python with OpenSSL 1.1 - run: | - cd /usr/local/src - sudo wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz - sudo tar -xzf Python-3.9.0.tgz - cd Python-3.9.0 - sudo ./configure --prefix=/opt/python3.9-openssl1.1 \ - --with-openssl=/opt/openssl-1.1 \ - --enable-optimizations - sudo make -j$(nproc) - sudo make altinstall - - - name: Test OpenSSL version - run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 - name: Install dependencies run: | @@ -207,10 +162,7 @@ jobs: - name: tox run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" - /opt/python3.9-openssl1.1/bin/python3.9 -m venv .venv - source .venv/bin/activate - tox -e py39-e2e + tox -e py312-e2e registry: name: E2E Registry Tests @@ -220,31 +172,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install deps and OpenSSL 1.1 - run: | - sudo apt-get -y update - sudo apt-get install -y wget build-essential - wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w - ./config --prefix=/opt/openssl-1.1 --openssldir=/opt/openssl-1.1 - sudo make install - - - name: Download Python and configure python with OpenSSL 1.1 - run: | - cd /usr/local/src - sudo wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz - sudo tar -xzf Python-3.9.0.tgz - cd Python-3.9.0 - sudo ./configure --prefix=/opt/python3.9-openssl1.1 \ - --with-openssl=/opt/openssl-1.1 \ - --enable-optimizations - sudo make -j$(nproc) - sudo make altinstall - - - name: Test OpenSSL version - run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 - name: Install dependencies run: | @@ -255,10 +186,7 @@ jobs: - name: tox run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" - /opt/python3.9-openssl1.1/bin/python3.9 -m venv .venv - source .venv/bin/activate - tox -e py39-registry + tox -e py312-registry cypress: name: Cypress Tests @@ -288,10 +216,10 @@ jobs: - name: Seed Database run: cd web && npm run quay:seed - - name: Set up Python 3.9 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.12 - name: Install dependencies run: | @@ -355,31 +283,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install deps and OpenSSL 1.1 - run: | - sudo apt-get -y update - sudo apt-get install -y wget build-essential - wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w - ./config --prefix=/opt/openssl-1.1 --openssldir=/opt/openssl-1.1 - sudo make install - - - name: Download Python and configure python with OpenSSL 1.1 - run: | - cd /usr/local/src - sudo wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz - sudo tar -xzf Python-3.9.0.tgz - cd Python-3.9.0 - sudo ./configure --prefix=/opt/python3.9-openssl1.1 \ - --with-openssl=/opt/openssl-1.1 \ - --enable-optimizations - sudo make -j$(nproc) - sudo make altinstall - - - name: Test OpenSSL version - run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 - name: Install dependencies run: | @@ -393,10 +300,7 @@ jobs: - name: tox run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" - /opt/python3.9-openssl1.1/bin/python3.9 -m venv .venv - source .venv/bin/activate - tox -e py39-mysql + tox -e py312-mysql psql: name: E2E Postgres Test @@ -406,31 +310,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Install deps and OpenSSL 1.1 - run: | - sudo apt-get -y update - sudo apt-get install -y wget build-essential - wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz - tar -xzf openssl-1.1.1w.tar.gz - cd openssl-1.1.1w - ./config --prefix=/opt/openssl-1.1 --openssldir=/opt/openssl-1.1 - sudo make install - - - name: Download Python and configure python with OpenSSL 1.1 - run: | - cd /usr/local/src - sudo wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz - sudo tar -xzf Python-3.9.0.tgz - cd Python-3.9.0 - sudo ./configure --prefix=/opt/python3.9-openssl1.1 \ - --with-openssl=/opt/openssl-1.1 \ - --enable-optimizations - sudo make -j$(nproc) - sudo make altinstall - - - name: Test OpenSSL version - run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 - name: Install dependencies run: | @@ -444,7 +327,4 @@ jobs: - name: tox run: | - /opt/python3.9-openssl1.1/bin/python3.9 -c "import ssl; print(ssl.OPENSSL_VERSION)" - /opt/python3.9-openssl1.1/bin/python3.9 -m venv .venv - source .venv/bin/activate - tox -e py39-psql + tox -e py312-psql diff --git a/Dockerfile b/Dockerfile index 5956e668a..6cc983e68 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,26 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal:latest AS base +FROM registry.access.redhat.com/ubi9/python-312:latest as base # Only set variables or install packages that need to end up in the # final container here. +USER root + ENV PATH=/app/bin/:$PATH \ - PYTHONUNBUFFERED=1 \ - PYTHONIOENCODING=UTF-8 \ - LC_ALL=C.UTF-8 \ - LANG=C.UTF-8 + PYTHON_VERSION=3.12 \ + PATH=$HOME/.local/bin/:$PATH \ + PYTHONUNBUFFERED=1 \ + PYTHONIOENCODING=UTF-8 \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 \ + CNB_STACK_ID=com.redhat.stacks.ubi9-python-312 \ + CNB_USER_ID=1001 \ + CNB_GROUP_ID=0 \ + PIP_NO_CACHE_DIR=off + ENV PYTHONUSERBASE /app ENV TZ UTC RUN set -ex\ - ; microdnf -y module enable nginx:1.22 \ - ; microdnf -y module enable python39:3.9 \ - ; microdnf update -y \ - ; microdnf -y --setopt=tsflags=nodocs install \ + ; dnf -y module enable nginx:1.22 \ + ; dnf update -y \ + ; dnf -y --setopt=tsflags=nodocs install \ dnsmasq \ memcached \ nginx \ @@ -20,13 +28,12 @@ RUN set -ex\ libjpeg-turbo \ openldap \ openssl \ - python39 \ python3-gpg \ + python3-six \ skopeo \ findutils \ - ; microdnf -y reinstall tzdata \ - ; microdnf remove platform-python-pip python39-pip \ - ; microdnf -y clean all && rm -rf /var/cache/yum + ; dnf -y reinstall tzdata \ + ; dnf -y clean all && rm -rf /var/cache/yum # Config-editor builds the javascript for the configtool. FROM registry.access.redhat.com/ubi8/nodejs-16 AS config-editor @@ -42,11 +49,11 @@ RUN set -ex\ FROM base AS build-python ENV PYTHONDONTWRITEBYTECODE 1 RUN set -ex\ - ; microdnf -y --setopt=tsflags=nodocs install \ + ; dnf -y --setopt=tsflags=nodocs install \ gcc-c++ \ git \ openldap-devel \ - python39-devel \ + python3.12-devel \ libffi-devel \ openssl-devel \ diffutils \ @@ -59,7 +66,7 @@ RUN set -ex\ libxml2-devel \ libxslt-devel \ freetype-devel \ - ; microdnf -y clean all + ; dnf -y clean all WORKDIR /build RUN python3 -m ensurepip --upgrade COPY requirements.txt . @@ -77,25 +84,27 @@ ENV CARGO_NET_GIT_FETCH_WITH_CLI=true # In Future if wget is to be removed , then uncomment below line for grpc installation on IBMZ i.e. s390x ENV GRPC_PYTHON_BUILD_SYSTEM_OPENSSL 1 +USER 1001 + RUN ARCH=$(uname -m) ; echo $ARCH; \ if [ "$ARCH" == "ppc64le" ] ; then \ GE_LATEST=$(grep "gevent" requirements.txt |cut -d "=" -f 3); \ wget https://github.com/IBM/oss-ecosystem-gevent/releases/download/${GE_LATEST}/manylinux_ppc64le_wheels_${GE_LATEST}.tar.gz; \ tar xvf manylinux_ppc64le_wheels_${GE_LATEST}.tar.gz; \ - python3 -m pip install --no-cache-dir --user wheelhouse/gevent-${GE_LATEST}-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl; \ + python3 -m pip install --no-cache-dir wheelhouse/gevent-${GE_LATEST}-cp39-cp3.12-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl; \ GRPC_LATEST=$(grep "grpcio" requirements.txt |cut -d "=" -f 3); \ wget https://github.com/IBM/oss-ecosystem-grpc/releases/download/${GRPC_LATEST}/grpcio-${GRPC_LATEST}-cp39-cp39-linux_ppc64le.whl; \ - python3 -m pip install --no-cache-dir --user grpcio-${GRPC_LATEST}-cp39-cp39-linux_ppc64le.whl; \ + python3 -m pip install --no-cache-dir grpcio-${GRPC_LATEST}-cp39-cp39-linux_ppc64le.whl; \ fi RUN set -ex\ - ; python3 -m pip install --no-cache-dir --progress-bar off --user $(grep -e '^pip=' -e '^wheel=' -e '^setuptools=' ./requirements.txt) \ - ; python3 -m pip install --no-cache-dir --progress-bar off --user --requirement requirements.txt \ + ; python3 -m pip install --no-cache-dir --progress-bar off $(grep -e '^pip=' -e '^wheel=' -e '^setuptools=' ./requirements.txt) \ + ; python3 -m pip install --no-cache-dir --progress-bar off --requirement requirements.txt \ ; RUN set -ex\ # Doing this is explicitly against the purpose and use of certifi. ; for dir in\ - $(find "$(python3 -m site --user-base)" -type d -name certifi)\ + $(find "/opt/app-root/lib/python3.12/site-packages" -type d -name certifi)\ ; do chgrp -R 0 "$dir" && chmod -R g=u "$dir" ; done\ ; @@ -169,7 +178,6 @@ ENV PYTHONPATH $QUAYPATH # Openshift runs a container as a random UID and GID 0, so anything # that's in the base image and needs to be modified at runtime needs # to make sure it's group-writable. -RUN alternatives --set python /usr/bin/python3 RUN set -ex\ ; setperms() { for d in "$@"; do chgrp -R 0 "$d" && chmod -R g=u "$d" && ls -ld "$d"; done; }\ ; newdir() { for d in "$@"; do mkdir -m 775 "$d" && ls -ld "$d"; done; }\ @@ -185,15 +193,26 @@ RUN set -ex\ ; ln -s $QUAYCONF /conf\ # Make a grip of runtime directories. ; newdir /certificates "$QUAYDIR" "$QUAYDIR/conf" "$QUAYDIR/conf/stack" /datastorage\ +# Another Openshift-ism: it doesn't bother picking a uid that means +# anything to the OS inside the container, so the process needs +# permissions to modify the user database. # Harden /etc/passwd – no group write. ; chown root:root /etc/passwd \ ; chmod 0644 /etc/passwd \ - ; + ; chown -R 1001:0 /etc/pki/ \ + ; chown -R 1001:0 /etc/ssl/ \ + ; chown -R 1001:0 /quay-registry \ + ; chmod ug+wx -R /etc/pki/ \ + ; chmod ug+wx -R /etc/ssl/ + + +RUN python3 -m pip install --no-cache-dir --progress-bar off dumb-init WORKDIR $QUAYDIR # Ordered from least changing to most changing. COPY --from=pushgateway /usr/local/bin/pushgateway /usr/local/bin/pushgateway -COPY --from=build-python /app /app +COPY --from=build-python /opt/app-root/lib/python3.12/site-packages /opt/app-root/lib/python3.12/site-packages +COPY --from=build-python /opt/app-root/bin /opt/app-root/bin COPY --from=config-tool /opt/app-root/src/go/bin/config-tool /bin COPY --from=build-quaydir /quaydir $QUAYDIR diff --git a/Makefile b/Makefile index 2e5acd1a6..ebcdc88dd 100644 --- a/Makefile +++ b/Makefile @@ -156,7 +156,7 @@ generate-proto-py: black: - black --line-length=100 --target-version=py39 --exclude "/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/" . + black --line-length=100 --target-version=py312 --exclude "/(\.eggs|\.git|\.hg|\.mypy_cache|\.nox|\.tox|\.venv|_build|buck-out|build|dist)/" . ################################# # Local Development Environment # diff --git a/conf/init/certs_install.sh b/conf/init/certs_install.sh index 1527f5771..a4f5f6d36 100755 --- a/conf/init/certs_install.sh +++ b/conf/init/certs_install.sh @@ -5,7 +5,7 @@ QUAYCONF=${QUAYCONF:-"$QUAYPATH/conf"} QUAYCONFIG=${QUAYCONFIG:-"$QUAYCONF/stack"} CERTDIR=${CERTDIR:-"$QUAYCONFIG/extra_ca_certs"} SYSTEM_CERTDIR=${SYSTEM_CERTDIR:-"/etc/pki/ca-trust/source/anchors"} -PYTHONUSERBASE_SITE_PACKAGE=${PYTHONUSERBASE_SITE_PACKAGE:-"$(python -m site --user-site)"} +PYTHONUSERBASE_SITE_PACKAGE=/opt/app-root/lib/python3.12/site-packages cd ${QUAYDIR:-"/quay-registry"} @@ -64,4 +64,11 @@ do done # Update all CA certificates. -update-ca-trust extract +# hack for UBI9, extract it a temp location and move +# to /etc/pki after because of permission issues + +mkdir -p /tmp/extracted +rm -rf /etc/pki/ca-trust/extracted +update-ca-trust extract -o /tmp/extracted +chmod ug+w -R /tmp/extracted +mv /tmp/extracted /etc/pki/ca-trust diff --git a/data/database.py b/data/database.py index 16415766c..b55cefbba 100644 --- a/data/database.py +++ b/data/database.py @@ -14,7 +14,7 @@ from datetime import datetime from enum import Enum, IntEnum, unique from random import SystemRandom -import rehash +import resumablesha256 as rehash import toposort from cachetools.func import lru_cache from peewee import * diff --git a/data/logs_model/test/test_logs_producer.py b/data/logs_model/test/test_logs_producer.py index b2575584d..0239f0684 100644 --- a/data/logs_model/test/test_logs_producer.py +++ b/data/logs_model/test/test_logs_producer.py @@ -5,8 +5,6 @@ import pytest from dateutil.parser import parse from mock import Mock, patch -from data.logs_model import configure - from .mock_elasticsearch import * from .test_elasticsearch import ( app_config, @@ -15,6 +13,7 @@ from .test_elasticsearch import ( mock_db_model, mock_elasticsearch, ) +from data.logs_model import configure logger = logging.getLogger(__name__) @@ -64,7 +63,7 @@ def test_kafka_logs_producers( producer_config = kafka_logs_producer_config with patch("kafka.client_async.KafkaClient.check_version"), patch( "kafka.KafkaProducer.send" - ) as mock_send, patch("kafka.KafkaProducer._max_usable_produce_magic"): + ) as mock_send, patch("kafka.KafkaProducer.max_usable_produce_magic"): configure(producer_config) logs_model.log_action( "pull_repo", diff --git a/data/secscan_model/interface.py b/data/secscan_model/interface.py index be230908f..16faa3518 100644 --- a/data/secscan_model/interface.py +++ b/data/secscan_model/interface.py @@ -1,6 +1,6 @@ from abc import ABCMeta, abstractmethod, abstractproperty -from deprecated import deprecated +from deprecation import deprecated from six import add_metaclass @@ -58,7 +58,7 @@ class SecurityScannerInterface(object): """ @abstractproperty - @deprecated(reason="Only exposed for the legacy notification worker") + @deprecated(details="Only exposed for the legacy notification worker") def legacy_api_handler(self): """ Exposes the legacy security scan API for legacy workers that need it or None if none. diff --git a/mypy.ini b/mypy.ini index 35cf31d65..5196a93ba 100644 --- a/mypy.ini +++ b/mypy.ini @@ -191,3 +191,9 @@ ignore_missing_imports = True [mypy-xhtml2pdf] ignore_missing_imports = True + +[mypy-resumablesha256] +ignore_missing_imports = True + +[mypy-deprecation] +ignore_missing_imports = True diff --git a/requirements.txt b/requirements.txt index d7e02c2b8..541276a62 100644 --- a/requirements.txt +++ b/requirements.txt @@ -49,7 +49,7 @@ jmespath==1.0.1 jsonpath-rw==1.4.0 jsonpointer==2.4 jsonschema==3.2.0 -kafka-python==1.4.7 +kafka-python==2.2.10 keystoneauth1==3.18.0 Mako==1.2.4 MarkupSafe==2.1.3 @@ -78,7 +78,7 @@ os-service-types==1.7.0 peewee==3.13.1 pillow==10.3.0 ply==3.11 -prometheus-client==0.7.1 +prometheus_client==0.7.1 protobuf==5.28.2 psutil==5.9.0 psycopg2==2.9.9 @@ -114,9 +114,9 @@ requests-oauthlib==1.3.1 rfc3986==1.3.2 s3transfer==0.7.0 semantic-version==2.10.0 -six==1.14.0 +six==1.17.0 soupsieve==1.9.5 -splunk-sdk==1.7.4 +splunk-sdk==2.1.0 SQLAlchemy==1.4.49 stevedore==5.1.0 stringscore==0.1.0 @@ -135,6 +135,8 @@ WebOb==1.8.8 websocket-client==1.7.0 Werkzeug==3.0.6 xhtml2pdf==0.2.6 +resumablesha256==1.0 +deprecation==2.1.0 # Leverage wheels for faster builds setuptools==71.1.0 @@ -164,7 +166,7 @@ tinycss2==1.2.1 tqdm==4.66.4 typing_extensions==4.8.0 tzdata==2023.3 -wrapt==1.13.3 +wrapt==1.17.2 zipp==3.19.2 zope.event==5.0 zope.interface==5.4.0 diff --git a/test/registry/registry_tests.py b/test/registry/registry_tests.py index a48ba7b55..f67399aa2 100644 --- a/test/registry/registry_tests.py +++ b/test/registry/registry_tests.py @@ -4,8 +4,6 @@ import hashlib import tarfile from io import BytesIO -import bencode -import rehash from werkzeug.datastructures import Accept from app import app as original_app diff --git a/test/test_keystone_auth.py b/test/test_keystone_auth.py index c04a0ebeb..82c617abc 100644 --- a/test/test_keystone_auth.py +++ b/test/test_keystone_auth.py @@ -222,6 +222,12 @@ def _create_app(requires_email=True): "interface": "admin", "id": "29beb2f1567642eb810b042b6719ea88", }, + { + "url": server_url + "/v3/identity", + "region": "RegionOne", + "interface": "public", + "id": "29beb2f1567642eb810b042b6719ea89", + }, ], "type": "identity", "id": "bd73972c0e14fb69bae8ff76e112a90", diff --git a/tox.ini b/tox.ini index 2c608c1af..f275d83e3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py39-{unit,registry,e2e,mysql,psql} +envlist = py312-{unit,registry,e2e,mysql,psql} skipsdist = True [pytest] @@ -39,7 +39,7 @@ environment = MYSQL_ALLOW_EMPTY_PASSWORD=1 MYSQL_USER=quay -[testenv:py39-unit] +[testenv:py312-unit] setenv = PYTHONDONTWRITEBYTECODE = 1 PYTHONPATH={toxinidir}{:}{toxinidir} @@ -52,7 +52,7 @@ commands = alembic upgrade head pytest --timeout=3600 -m {env:MARKERS} --exitfirst --ignore=buildman/test/test_buildman.py -vv {env:FILE:} {posargs} -[testenv:py39-mysql] +[testenv:py312-mysql] setenv = PYTHONDONTWRITEBYTECODE = 1 PYTHONPATH={toxinidir}{:}{toxinidir} @@ -80,7 +80,7 @@ environment = POSTGRES_PASSWORD=quay POSTGRES_USER=quay -[testenv:py39-psql] +[testenv:py312-psql] # TODO(kleesc): Re-enable buildman tests after buildman rewrite setenv = PYTHONDONTWRITEBYTECODE = 1 diff --git a/util/registry/generatorfile.py b/util/registry/generatorfile.py index bd671b501..286723fc8 100644 --- a/util/registry/generatorfile.py +++ b/util/registry/generatorfile.py @@ -54,6 +54,13 @@ class GeneratorFile(object): def flush(self): _complain_ifclosed(self._closed) + @property + def closed(self): + """ + Return whether the file is closed. + """ + return self._closed + def read(self, size=-1): """ Read at most size bytes from the file (less if the read hits EOF before obtaining size