Skip to content

Commit

Permalink
Merge pull request #672 from freyes/py312-support
Browse files Browse the repository at this point in the history
Python 3.12 support
  • Loading branch information
ajkavanagh authored Apr 19, 2024
2 parents 52eb3a6 + 4988527 commit e0592a6
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 23 deletions.
54 changes: 50 additions & 4 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10']
python-version:
- '3.8' # focal
- '3.10' # jammy
- '3.12' # noble
steps:
- name: Check out code
uses: actions/checkout@v2
Expand Down Expand Up @@ -39,6 +42,11 @@ jobs:
path: ${{ steps.snap-build.outputs.snap }}

integration:
strategy:
matrix:
charmcraft_channel:
- "2.x/stable"
- "3.x/beta"
name: Integration test
needs: build
runs-on: ubuntu-latest
Expand All @@ -64,17 +72,19 @@ jobs:
name: charm-snap
path: tests/charm-minimal/charm-snap

- name: Build reactive charm with charmcraft
- name: Build reactive charm with charmcraft-2.x
if: ${{ matrix.charmcraft_channel == '2.x/stable' }}
run: |
set -euxo pipefail
sudo snap install --classic --channel latest/edge charmcraft
sudo snap install --classic --channel ${{ matrix.charmcraft_channel }} charmcraft
cat << EOF | tee tests/charm-minimal/charmcraft.yaml
type: charm
parts:
charm-tools:
plugin: nil
override-build: |
snap install --dangerous --classic \$CRAFT_PROJECT_DIR/parts/charm/src/charm-snap/*.snap
ls -lR \$CRAFT_PROJECT_DIR/
snap install --dangerous --classic /root/project/charm-snap/charm_0.0.0_amd64.snap
rm -rf \$CRAFT_PROJECT_DIR/parts/charm/src/charm-snap
charm:
after: [charm-tools]
Expand All @@ -99,6 +109,41 @@ jobs:
architectures: [amd64]
EOF
charmcraft pack -p tests/charm-minimal -v
- name: Build reactive charm with charmcraft-3.x
if: ${{ matrix.charmcraft_channel == '3.x/beta' }}
run: |
set -euxo pipefail
sudo snap install --classic --channel ${{ matrix.charmcraft_channel }} charmcraft
cat << EOF | tee tests/charm-minimal/charmcraft.yaml
type: charm
parts:
charm-tools:
plugin: nil
override-build: |
ls -lR \$CRAFT_PROJECT_DIR/
snap install --dangerous --classic /root/project/charm-snap/charm_0.0.0_amd64.snap
rm -rf \$CRAFT_PROJECT_DIR/parts/charm/src/charm-snap
charm:
after: [charm-tools]
source: .
plugin: reactive
reactive-charm-build-arguments:
- -v
- --binary-wheels-from-source
- --upgrade-buildvenv-core-deps
build-packages:
- python3-dev
- libpq-dev
base: [email protected]
platforms:
amd64:
EOF
charmcraft pack -p tests/charm-minimal -v
mv minimal_amd64.charm minimal_ubuntu-24.04-amd64.charm
## action to interactively debug CI failures.
# - name: Setup upterm session
# if: failure()
# uses: lhotari/action-upterm@v1
- name: Upload charmcraft execution logs
if: always()
uses: actions/upload-artifact@v3
Expand All @@ -113,3 +158,4 @@ jobs:
minimal_ubuntu-18.04-amd64.charm
minimal_ubuntu-20.04-amd64.charm
minimal_ubuntu-22.04-amd64.charm
minimal_ubuntu-24.04-amd64.charm
7 changes: 5 additions & 2 deletions charmtools/build/tactics.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from inspect import getargspec
from inspect import getfullargspec
import errno
import json
import logging
Expand Down Expand Up @@ -43,7 +43,7 @@ def get(cls, entity, target, layer, next_config, current_config,
given entity.
"""
for candidate in current_config.tactics + DEFAULT_TACTICS:
argspec = getargspec(candidate.trigger)
argspec = getfullargspec(candidate.trigger)
if len(argspec.args) == 2:
# old calling convention
name = candidate.__name__
Expand Down Expand Up @@ -1253,6 +1253,9 @@ def __call__(self):
).exit_on_error()()
if self.upgrade_deps:
utils.upgrade_venv_core_packages(self._venv, env=self._get_env())
elif utils.get_python_version(self._venv,
env=self._get_env()) >= utils.PY312:
log.debug('Skip pinning of setuptools, because Python>=3.12')
else:
utils.pin_setuptools_for_pep440(self._venv, env=self._get_env())
log.debug(
Expand Down
26 changes: 24 additions & 2 deletions charmtools/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from path import Path as path

log = logging.getLogger('utils')
PY312 = (3, 12, 0)


@contextmanager
Expand Down Expand Up @@ -681,8 +682,8 @@ def pin_setuptools_for_pep440(venv_dir, env=None):
:type env: Optional[Dict[str,str]]
:returns: This function is called for its side effect
"""
log.debug('Pinning setuptools < 67 for pep440 non compliant packages "{}"'
.format(venv_dir))
log.info('Pinning setuptools < 67 for pep440 non compliant packages "{}"'
.format(venv_dir))
Process((os.path.join(venv_dir, 'bin/pip'),
'install', '-U', 'pip<23.1', 'setuptools<67'),
env=env).exit_on_error()()
Expand All @@ -702,3 +703,24 @@ def get_venv_package_list(venv_dir, env=None):
if result:
return result.output
result.exit_on_error()


def get_python_version(venv_dir, env=None):
"""Get the Python interpreter version in the virtualenv.
:param venv_dir: Full path to virtualenv in which packages will be listed
:type venv_dir: str
:param env: Environment to use when executing command
:type env: Optional[Dict[str,str]]
:returns: Tuple with major, minor and microversion
:rtype: Tuple[str]
"""
result = Process((os.path.join(venv_dir, 'bin/python3'), '--version'),
env=env)()
m = re.match(r'^Python[ ]+(\d+)\.(\d+)\.(\d+).*', result.output)
if not m:
raise ValueError('Cannot identify the python version: %s' % result)

return (int(m.group(1)),
int(m.group(2)),
int(m.group(3)))
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
'pathspec<0.11;python_version >= "3.7"',
'otherstuf<=1.1.0',
'path.py>=10.5,<13',
'pip>=1.5.4,<23',
'pip>=1.5.4',
'jujubundlelib<0.6',
'virtualenv>=1.11.4,<21',
'colander<1.9',
Expand Down
22 changes: 11 additions & 11 deletions tests/test_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,10 @@ def test_tester_layer(self, pv):
cyaml = base / "layer.yaml"
self.assertTrue(cyaml.exists())
cyaml_data = yaml.safe_load(cyaml.open())
self.assertEquals(cyaml_data['includes'], ['layers/test-base',
self.assertEqual(cyaml_data['includes'], ['layers/test-base',
'layers/mysql'])
self.assertEquals(cyaml_data['is'], 'foo')
self.assertEquals(cyaml_data['options']['mysql']['qux'], 'one')
self.assertEqual(cyaml_data['is'], 'foo')
self.assertEqual(cyaml_data['options']['mysql']['qux'], 'one')

self.assertTrue((base / "hooks/config-changed").exists())

Expand All @@ -205,13 +205,13 @@ def test_tester_layer(self, pv):
sigs = base / ".build.manifest"
self.assertTrue(sigs.exists())
data = json.load(sigs.open())
self.assertEquals(data['signatures']["README.md"], [
self.assertEqual(data['signatures']["README.md"], [
u'foo',
"static",
u'cfac20374288c097975e9f25a0d7c81783acdbc81'
'24302ff4a731a4aea10de99'])

self.assertEquals(data["signatures"]['metadata.yaml'], [
self.assertEqual(data["signatures"]['metadata.yaml'], [
u'foo',
"dynamic",
u'12c1f6fc865da0660f6dc044cca03b0244e883d9a99fdbdfab6ef6fc2fed63b7'
Expand Down Expand Up @@ -588,9 +588,9 @@ def test_layer_options(self, log):
},
})

@mock.patch('charmtools.build.tactics.getargspec')
@mock.patch('charmtools.build.tactics.getfullargspec')
@mock.patch('charmtools.utils.walk')
def test_custom_tactics(self, mwalk, mgetargspec):
def test_custom_tactics(self, mwalk, mgetfullargspec):
def _layer(tactics):
return mock.Mock(config=build.builder.BuildConfig({'tactics':
tactics}),
Expand All @@ -611,13 +611,13 @@ def _layer(tactics):
builder.plan_layers(layers, {})
calls = [call[1]['current_config'].tactics
for call in mwalk.call_args_list]
self.assertEquals(calls, [
self.assertEqual(calls, [
['first'],
['second', 'first'],
['third', 'second', 'first'],
])

mgetargspec.return_value = mock.Mock(args=[1, 2, 3, 4])
mgetfullargspec.return_value = mock.Mock(args=[1, 2, 3, 4])
current_config = mock.Mock(tactics=[
mock.Mock(name='1', **{'trigger.return_value': False}),
mock.Mock(name='2', **{'trigger.return_value': False}),
Expand All @@ -629,9 +629,9 @@ def _layer(tactics):
mock.Mock(),
current_config,
mock.Mock())
self.assertEquals([t.trigger.called for t in current_config.tactics],
self.assertEqual([t.trigger.called for t in current_config.tactics],
[True, True, True])
self.assertEquals([t.called for t in current_config.tactics],
self.assertEqual([t.called for t in current_config.tactics],
[False, False, True])


Expand Down
9 changes: 9 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,12 @@ def test_get_venv_package_list(self, mock_Process, mock_sys_exit):
mock_Process().return_value = utils.ProcessResult('fakecmd', 1, '', '')
utils.get_venv_package_list('/some/dir', env={'some': 'envvar'})
mock_sys_exit.assert_called_once_with(1)

@unittest.mock.patch.object(utils, "Process")
def test_get_oython_version(self, process_klass):
process_klass().return_value = utils.ProcessResult(
['python3', '--version'], 0, b'Python 3.12.4', b'')
self.assertEqual(
utils.get_python_version('/some/dir', env={'some': 'envvar'}),
(3, 12, 4)
)
4 changes: 1 addition & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[tox]
envlist = py3
skipsdist = true
package = editable

[testenv]
install_command = pip install {opts} {packages}
Expand All @@ -18,8 +18,6 @@ deps =
coverage
mock
responses
-e {toxinidir}
https://github.com/openstack-charmers/charm-templates-openstack/archive/master.zip#egg=charm_templates_openstack
ipdb

[testenv:lint]
Expand Down

0 comments on commit e0592a6

Please sign in to comment.