Skip to content

Commit

Permalink
add support for python 3.12
Browse files Browse the repository at this point in the history
  • Loading branch information
branchvincent committed Dec 28, 2023
1 parent ae3ab8e commit 7278f45
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 84 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ jobs:
- python-version: '3.10'
os: ubuntu-latest
tox-env: py
- python-version: '3.11'
os: ubuntu-latest
tox-env: py
- python-version: '3.12'
os: ubuntu-latest
tox-env: py

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -74,6 +80,10 @@ jobs:
tox-env: py
- python-version: '3.10'
tox-env: py
- python-version: '3.11'
tox-env: py
- python-version: '3.12'
tox-env: py

steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -108,6 +118,10 @@ jobs:
tox-env: py
- python-version: '3.10'
tox-env: py
- python-version: '3.11'
tox-env: py
- python-version: '3.12'
tox-env: py

steps:
- uses: actions/checkout@v2
Expand Down
4 changes: 4 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
install_requires=[
'click',
'docker',
'importlib-metadata; python_version < "3.8"',
'packaging',
'pip',
'PyYAML',
'retrying',
Expand All @@ -52,6 +54,8 @@
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Operating System :: OS Independent',
'Environment :: Console',
'Topic :: Internet :: WWW/HTTP',
Expand Down
7 changes: 4 additions & 3 deletions shub/deploy_egg.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import absolute_import
import os
import tempfile
from shutil import which

import click

Expand All @@ -9,7 +10,7 @@
from shub.exceptions import (BadParameterException, NotFoundException,
SubcommandException)
from shub.utils import (decompress_egg_files, download_from_pypi,
find_executable, run_cmd)
run_cmd)


HELP = """
Expand Down Expand Up @@ -86,7 +87,7 @@ def _checkout(repo, git_branch=None, target_dir='egg-tmp-clone'):
]
missing_exes = []
for cmd in vcs_commands:
exe = find_executable(cmd[0])
exe = which(cmd[0])
if not exe:
missing_exes.append(cmd[0])
continue
Expand All @@ -109,7 +110,7 @@ def _checkout(repo, git_branch=None, target_dir='egg-tmp-clone'):

if git_branch:
try:
run_cmd([find_executable('git'), 'checkout', git_branch])
run_cmd([which('git'), 'checkout', git_branch])
except SubcommandException:
raise BadParameterException("Branch %s is not valid" % git_branch)
click.echo("%s branch was checked out" % git_branch)
Expand Down
4 changes: 2 additions & 2 deletions shub/image/run/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import logging
import datetime
from multiprocessing import Process
from distutils.spawn import find_executable
from shutil import which


def _consume_from_fifo(fifo_path):
Expand Down Expand Up @@ -68,7 +68,7 @@ def main():
# non-daemon to allow it to finish reading from pipe before exit.
Process(target=_consume_from_fifo, args=[fifo_path]).start()
# replace current process with original start-crawl
os.execv(find_executable('start-crawl'), sys.argv)
os.execv(which('start-crawl'), sys.argv)

Check warning on line 71 in shub/image/run/wrapper.py

View check run for this annotation

Codecov / codecov/patch

shub/image/run/wrapper.py#L71

Added line #L71 was not covered by tests


if __name__ == '__main__':
Expand Down
10 changes: 7 additions & 3 deletions shub/image/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import yaml
from tqdm import tqdm
from six import binary_type
import pkg_resources

from shub import config as shub_config
from shub import utils as shub_utils
Expand All @@ -18,6 +17,11 @@
ShubDeprecationWarning, print_warning, BadParameterException,
)

if sys.version_info < (3, 8):
import importlib_metadata as metadata
else:
from importlib import metadata


DEFAULT_DOCKER_API_VERSION = '1.21'
STATUS_FILE_LOCATION = '.releases'
Expand Down Expand Up @@ -83,8 +87,8 @@ def get_docker_client(validate=True):
import docker
except ImportError:
raise ImportError(DOCKER_PY_UNAVAILABLE_MSG)
for dep in pkg_resources.working_set:
if dep.project_name == 'docker-py':
for dep in metadata.distributions():
if dep.name == 'docker-py':
raise ImportError(DOCKER_PY_UNAVAILABLE_MSG)

docker_host = os.environ.get('DOCKER_HOST')
Expand Down
59 changes: 31 additions & 28 deletions shub/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
import time

from collections import deque
from six.moves.configparser import SafeConfigParser
from distutils.spawn import find_executable
from distutils.version import LooseVersion, StrictVersion
from configparser import ConfigParser
from shutil import which
from packaging.version import Version
from glob import glob
from importlib import import_module
from tempfile import NamedTemporaryFile, TemporaryFile
Expand Down Expand Up @@ -143,17 +143,21 @@ def _check_deploy_files_size(files):

def write_and_echo_logs(keep_log, last_logs, rsp, verbose):
"""It will write logs to temporal file and echo if verbose is True."""
with NamedTemporaryFile(prefix='shub_deploy_', suffix='.log',
delete=(not keep_log)) as log_file:
for line in rsp.iter_lines():
if verbose:
click.echo(line)
last_logs.append(line)
log_file.write(line + b'\n')
log_contents = b""
for line in rsp.iter_lines():
if verbose:
click.echo(line)
last_logs.append(line)
log_contents += line + b'\n'
deployed = _is_deploy_successful(last_logs)
if not deployed:
keep_log = True
echo_short_log_if_deployed(deployed, last_logs, verbose=verbose)

deployed = _is_deploy_successful(last_logs)
echo_short_log_if_deployed(deployed, last_logs, log_file, verbose)
if not log_file.delete:
with NamedTemporaryFile(prefix='shub_deploy_', suffix='.log',
delete=not keep_log) as log_file:
log_file.write(log_contents)
if keep_log:
click.echo("Deploy log location: %s" % log_file.name)
if not deployed:
try:
Expand All @@ -163,12 +167,11 @@ def write_and_echo_logs(keep_log, last_logs, rsp, verbose):
raise RemoteErrorException("Deploy failed: {}".format(last_log))


def echo_short_log_if_deployed(deployed, last_logs, log_file, verbose):
def echo_short_log_if_deployed(deployed, last_logs, log_file=None, verbose=False):
if deployed:
if not verbose:
click.echo(last_logs[-1])
else:
log_file.delete = False
if not verbose:
click.echo("Deploy log last %s lines:" % len(last_logs))
for line in last_logs:
Expand Down Expand Up @@ -212,7 +215,7 @@ def patch_sys_executable():


def find_exe(exe_name):
exe = find_executable(exe_name)
exe = which(exe_name)
if not exe:
raise NotFoundException("Please install {}".format(exe_name))
return exe
Expand Down Expand Up @@ -275,7 +278,7 @@ def pwd_version():


def pwd_git_version():
git = find_executable('git')
git = which('git')
if not git:
return None
try:
Expand All @@ -290,7 +293,7 @@ def pwd_git_version():


def pwd_hg_version():
hg = find_executable('hg')
hg = which('hg')
if not hg:
return None
try:
Expand All @@ -302,7 +305,7 @@ def pwd_hg_version():


def pwd_bzr_version():
bzr = find_executable('bzr')
bzr = which('bzr')
if not bzr:
return None
try:
Expand Down Expand Up @@ -485,9 +488,9 @@ def inside_project():


def get_config(use_closest=True):
"""Get Scrapy config file as a SafeConfigParser"""
"""Get Scrapy config file as a ConfigParser"""
sources = get_sources(use_closest)
cfg = SafeConfigParser()
cfg = ConfigParser()
cfg.read(sources)
return cfg

Expand All @@ -506,7 +509,7 @@ def get_sources(use_closest=True):


def get_scrapycfg_targets(cfgfiles=None):
cfg = SafeConfigParser()
cfg = ConfigParser()
cfg.read(cfgfiles or [])
baset = dict(cfg.items('deploy')) if cfg.has_section('deploy') else {}
targets = {}
Expand Down Expand Up @@ -627,8 +630,8 @@ def update_available(silent_fail=True):
"""
try:
release_data = latest_github_release()
latest_rls = StrictVersion(release_data['name'].lstrip('v'))
used_rls = StrictVersion(shub.__version__)
latest_rls = Version(release_data['name'].lstrip('v'))
used_rls = Version(shub.__version__)
if used_rls >= latest_rls:
return None
return release_data['html_url']
Expand All @@ -643,15 +646,15 @@ def download_from_pypi(dest, pkg=None, reqfile=None, extra_args=None):
if (not pkg and not reqfile) or (pkg and reqfile):
raise ValueError('Call with either pkg or reqfile')
extra_args = extra_args or []
pip_version = LooseVersion(getattr(pip, '__version__', '1.0'))
pip_version = Version(getattr(pip, '__version__', '1.0'))
cmd = 'install'
no_wheel = []
target = [pkg] if pkg else ['-r', reqfile]
if pip_version >= LooseVersion('1.4'):
if pip_version >= Version('1.4'):
no_wheel = ['--no-use-wheel']
if pip_version >= LooseVersion('7'):
if pip_version >= Version('7'):
no_wheel = ['--no-binary=:all:']
if pip_version >= LooseVersion('8'):
if pip_version >= Version('8'):
cmd = 'download'
with patch_sys_executable():
pip_main([cmd, '-d', dest, '--no-deps'] + no_wheel + extra_args +
Expand Down
18 changes: 9 additions & 9 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def test_load(self):
}
self.assertEqual(projects, self.conf.projects)
endpoints = {'external': 'ext_endpoint'}
self.assertDictContainsSubset(endpoints, self.conf.endpoints)
self.assertLessEqual(endpoints.items(), self.conf.endpoints.items())
apikeys = {'default': 'key', 'otheruser': 'otherkey'}
self.assertEqual(apikeys, self.conf.apikeys)
stacks = {'dev': 'scrapy:v1.1'}
Expand All @@ -145,7 +145,7 @@ def test_load_partial(self):
"""
conf = self._get_conf_with_yml(yml)
endpoints = {'external': 'ext_endpoint'}
self.assertDictContainsSubset(endpoints, conf.endpoints)
self.assertLessEqual(endpoints.items(), conf.endpoints.items())
self.assertEqual(conf.projects, {})
self.assertEqual(conf.apikeys, {})
self.assertEqual(conf.images, {})
Expand Down Expand Up @@ -176,9 +176,9 @@ def test_load_shortcut_mixed(self):
dev: dev_stack
stack: prod_stack
"""
self.assertDictContainsSubset(
self._get_conf_with_yml(yml).stacks,
{'default': 'prod_stack', 'dev': 'dev_stack'},
self.assertLessEqual(
self._get_conf_with_yml(yml).stacks.items(),
{'default': 'prod_stack', 'dev': 'dev_stack'}.items()
)

def test_load_shortcut_conflict(self):
Expand Down Expand Up @@ -586,7 +586,7 @@ def test_get_image_ambiguous_global_image_and_global_stack(self):
image: true
stack: scrapy:1.3
""")
with self.assertRaisesRegexp(BadConfigException, '(?i)ambiguous'):
with self.assertRaisesRegex(BadConfigException, '(?i)ambiguous'):
self.conf.get_image('default')

def test_get_image_ambiguous_global_image_and_project_stack(self):
Expand All @@ -601,9 +601,9 @@ def test_get_image_ambiguous_global_image_and_project_stack(self):
stack: scrapy:1.3
image: true
""")
with self.assertRaisesRegexp(BadConfigException, '(?i)ambiguous'):
with self.assertRaisesRegex(BadConfigException, '(?i)ambiguous'):
self.conf.get_image('bad')
with self.assertRaisesRegexp(BadConfigException, '(?i)disabled'):
with self.assertRaisesRegex(BadConfigException, '(?i)disabled'):
self.conf.get_image('good')

def test_get_image_ambiguous_project_image_and_project_stack(self):
Expand All @@ -614,7 +614,7 @@ def test_get_image_ambiguous_project_image_and_project_stack(self):
image: true
stack: scrapy:1.3
""")
with self.assertRaisesRegexp(BadConfigException, '(?i)ambiguous'):
with self.assertRaisesRegex(BadConfigException, '(?i)ambiguous'):
self.conf.get_image('default')

def test_get_target_conf(self):
Expand Down
12 changes: 6 additions & 6 deletions tests/test_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ def test_forwards_args_and_settings(self, mock_client):
"--argument ARGWITHEQUAL=val2=val2".split(' '),
)
job_args = mock_proj.jobs.run.call_args[1]['job_args']
self.assertDictContainsSubset(
{'ARG': 'val1', 'ARGWITHEQUAL': 'val2=val2'},
job_args,
self.assertLessEqual(
{'ARG': 'val1', 'ARGWITHEQUAL': 'val2=val2'}.items(),
job_args.items(),
)
job_settings = mock_proj.jobs.run.call_args[1]['job_settings']
self.assertEqual(
Expand Down Expand Up @@ -116,9 +116,9 @@ def test_forwards_environment(self, mock_client):
"testspider -e VAR1=VAL1 --environment VAR2=VAL2".split(' '),
)
call_kwargs = mock_proj.jobs.run.call_args[1]
self.assertDictContainsSubset(
{'VAR1': 'VAL1', 'VAR2': 'VAL2'},
call_kwargs['environment'],
self.assertLessEqual(
{'VAR1': 'VAL1', 'VAR2': 'VAL2'}.items(),
call_kwargs['environment'].items(),
)


Expand Down
Loading

0 comments on commit 7278f45

Please sign in to comment.