Skip to content

Commit cbf4796

Browse files
committed
Merge commit '154331e455a3153252e44fe41a9e0b3d08ccf477' into release-v0.3.27
2 parents f77db1d + 154331e commit cbf4796

File tree

25 files changed

+2522
-260
lines changed

25 files changed

+2522
-260
lines changed

.ci/bash_functions

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# shellcheck shell=bash
2+
3+
# Tox environment name -> Python executable name (e.g. py312-m_mtg -> python3.12)
4+
toxenv-python() {
5+
local pattern='^py([23])([0-9]{1,2}).*'
6+
if [[ $1 =~ $pattern ]]; then
7+
echo "python${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
8+
return
9+
else
10+
echo "${FUNCNAME[0]}: $1: environment name not recognised" >&2
11+
return 1
12+
fi
13+
}

.ci/show_python_versions

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
set -o errexit -o nounset -o pipefail
3+
4+
INDENT=" "
5+
POSSIBLE_PYTHONS=(
6+
python
7+
python2
8+
python3
9+
/usr/bin/python
10+
/usr/bin/python2
11+
/usr/bin/python3
12+
# GitHub macOS 12 images: python2.7 is installed, but not on $PATH
13+
/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7
14+
)
15+
16+
for p in "${POSSIBLE_PYTHONS[@]}"; do
17+
echo "$p"
18+
if [[ ${p:0:1} == "/" && -e $p ]]; then
19+
:
20+
elif type "$p" > /dev/null 2>&1; then
21+
type "$p" 2>&1 | sed -e "s/^/${INDENT}type: /"
22+
else
23+
echo "${INDENT}Not present"
24+
echo
25+
continue
26+
fi
27+
28+
$p -c "import sys; print('${INDENT}version: %d.%d.%d' % sys.version_info[:3])"
29+
# macOS builders lack a realpath command
30+
$p -c "import os.path; print('${INDENT}realpath: %s' % os.path.realpath('$(type -p "$p")'))"
31+
$p -c "import sys; print('${INDENT}sys.executable: %s' % sys.executable)"
32+
echo
33+
done

.github/workflows/tests.yml

Lines changed: 24 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,10 @@ jobs:
2929
- tox_env: py27-m_ans-ans4
3030

3131
- tox_env: py36-m_ans-ans2.10
32-
python_version: '3.6'
3332
- tox_env: py36-m_ans-ans4
34-
python_version: '3.6'
3533

3634
- tox_env: py27-m_mtg
3735
- tox_env: py36-m_mtg
38-
python_version: '3.6'
3936

4037
steps:
4138
- uses: actions/checkout@v4
@@ -44,90 +41,38 @@ jobs:
4441
registry: ghcr.io
4542
username: ${{ github.actor }}
4643
password: ${{ secrets.GITHUB_TOKEN }}
47-
- name: Install build deps
44+
- run: .ci/show_python_versions
45+
- name: Install deps
46+
id: install-deps
4847
run: |
4948
set -o errexit -o nounset -o pipefail
50-
51-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
52-
53-
if [[ -z $PYTHON ]]; then
54-
echo 1>&2 "Python interpreter could not be determined"
55-
exit 1
56-
fi
49+
source .ci/bash_functions
50+
PYTHON="$(toxenv-python '${{ matrix.tox_env }}')"
5751
5852
sudo apt-get update
5953
6054
if [[ $PYTHON == "python2.7" ]]; then
6155
sudo apt install -y python2-dev sshpass virtualenv
62-
elif [[ $PYTHON == "python3.6" ]]; then
63-
sudo apt install -y gcc-10 make libbz2-dev liblzma-dev libreadline-dev libsqlite3-dev libssl-dev sshpass virtualenv zlib1g-dev
64-
curl --fail --silent --show-error --location https://pyenv.run | bash
65-
CC=gcc-10 ~/.pyenv/bin/pyenv install --force 3.6
66-
else
67-
echo 1>&2 "Python interpreter $PYTHON not available"
68-
exit 1
69-
fi
70-
- name: Show Python versions
71-
run: |
72-
set -o errexit -o nounset -o pipefail
73-
74-
# macOS builders lack a realpath command
75-
type python && python -c"import os.path;print(os.path.realpath('$(type -p python)'))" && python --version
76-
type python2 && python2 -c"import os.path;print(os.path.realpath('$(type -p python2)'))" && python2 --version
77-
type python3 && python3 -c"import os.path;print(os.path.realpath('$(type -p python3)'))" && python3 --version
78-
echo
79-
80-
if [ -e /usr/bin/python ]; then
81-
echo "/usr/bin/python: sys.executable: $(/usr/bin/python -c 'import sys; print(sys.executable)')"
82-
fi
83-
84-
if [ -e /usr/bin/python2 ]; then
85-
echo "/usr/bin/python2: sys.executable: $(/usr/bin/python2 -c 'import sys; print(sys.executable)')"
86-
fi
87-
88-
if [ -e /usr/bin/python2.7 ]; then
89-
echo "/usr/bin/python2.7: sys.executable: $(/usr/bin/python2.7 -c 'import sys; print(sys.executable)')"
90-
fi
91-
- name: Install tooling
92-
run: |
93-
set -o errexit -o nounset -o pipefail
94-
95-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
96-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
97-
98-
if [[ -z $PYTHON ]]; then
99-
echo 1>&2 "Python interpreter could not be determined"
100-
exit 1
101-
fi
102-
103-
if [[ $PYTHON == "python2.7" ]]; then
10456
curl "https://bootstrap.pypa.io/pip/2.7/get-pip.py" --output "get-pip.py"
10557
"$PYTHON" get-pip.py --user --no-python-version-warning
10658
# Avoid Python 2.x pip masking system pip
10759
rm -f ~/.local/bin/{easy_install,pip,wheel}
10860
elif [[ $PYTHON == "python3.6" ]]; then
61+
sudo apt install -y gcc-10 make libbz2-dev liblzma-dev libreadline-dev libsqlite3-dev libssl-dev sshpass virtualenv zlib1g-dev
62+
curl --fail --silent --show-error --location https://pyenv.run | bash
63+
CC=gcc-10 ~/.pyenv/bin/pyenv install --force 3.6
10964
PYTHON="$HOME/.pyenv/versions/3.6.15/bin/python3.6"
11065
fi
11166
11267
"$PYTHON" -m pip install -r "tests/requirements-tox.txt"
68+
echo "python=$PYTHON" >> $GITHUB_OUTPUT
11369
- name: Run tests
11470
env:
11571
GITHUB_ACTOR: ${{ github.actor }}
11672
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11773
run: |
11874
set -o errexit -o nounset -o pipefail
119-
120-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
121-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
122-
123-
if [[ -z $PYTHON ]]; then
124-
echo 1>&2 "Python interpreter could not be determined"
125-
exit 1
126-
fi
127-
128-
if [[ $PYTHON == "python3.6" ]]; then
129-
PYTHON="$HOME/.pyenv/versions/3.6.15/bin/python3.6"
130-
fi
75+
PYTHON="${{ steps.install-deps.outputs.python }}"
13176
13277
"$PYTHON" -m tox -e "${{ matrix.tox_env }}"
13378
@@ -183,60 +128,26 @@ jobs:
183128
registry: ghcr.io
184129
username: ${{ github.actor }}
185130
password: ${{ secrets.GITHUB_TOKEN }}
186-
- name: Install build deps
131+
- run: .ci/show_python_versions
132+
- name: Install deps
133+
id: install-deps
187134
run: |
188135
set -o errexit -o nounset -o pipefail
136+
source .ci/bash_functions
137+
PYTHON="$(toxenv-python '${{ matrix.tox_env }}')"
189138
190139
sudo apt-get update
191140
sudo apt-get install -y sshpass virtualenv
192-
- name: Show Python versions
193-
run: |
194-
set -o errexit -o nounset -o pipefail
195-
196-
# macOS builders lack a realpath command
197-
type python && python -c"import os.path;print(os.path.realpath('$(type -p python)'))" && python --version
198-
type python2 && python2 -c"import os.path;print(os.path.realpath('$(type -p python2)'))" && python2 --version
199-
type python3 && python3 -c"import os.path;print(os.path.realpath('$(type -p python3)'))" && python3 --version
200-
echo
201-
202-
if [ -e /usr/bin/python ]; then
203-
echo "/usr/bin/python: sys.executable: $(/usr/bin/python -c 'import sys; print(sys.executable)')"
204-
fi
205-
206-
if [ -e /usr/bin/python2 ]; then
207-
echo "/usr/bin/python2: sys.executable: $(/usr/bin/python2 -c 'import sys; print(sys.executable)')"
208-
fi
209-
210-
if [ -e /usr/bin/python2.7 ]; then
211-
echo "/usr/bin/python2.7: sys.executable: $(/usr/bin/python2.7 -c 'import sys; print(sys.executable)')"
212-
fi
213-
- name: Install tooling
214-
run: |
215-
set -o errexit -o nounset -o pipefail
216-
217-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
218-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
219-
220-
if [[ -z $PYTHON ]]; then
221-
echo 1>&2 "Python interpreter could not be determined"
222-
exit 1
223-
fi
224141
225142
"$PYTHON" -m pip install -r "tests/requirements-tox.txt"
143+
echo "python=$PYTHON" >> $GITHUB_OUTPUT
226144
- name: Run tests
227145
env:
228146
GITHUB_ACTOR: ${{ github.actor }}
229147
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
230148
run: |
231149
set -o errexit -o nounset -o pipefail
232-
233-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
234-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
235-
236-
if [[ -z $PYTHON ]]; then
237-
echo 1>&2 "Python interpreter could not be determined"
238-
exit 1
239-
fi
150+
PYTHON="${{ steps.install-deps.outputs.python }}"
240151
241152
"$PYTHON" -m tox -e "${{ matrix.tox_env }}"
242153
@@ -265,61 +176,25 @@ jobs:
265176
with:
266177
python-version: ${{ matrix.python_version }}
267178
if: ${{ matrix.python_version }}
268-
- name: Show Python versions
269-
run: |
270-
set -o errexit -o nounset -o pipefail
271-
272-
# macOS builders lack a realpath command
273-
type python && python -c"import os.path;print(os.path.realpath('$(type -p python)'))" && python --version
274-
type python2 && python2 -c"import os.path;print(os.path.realpath('$(type -p python2)'))" && python2 --version
275-
type python3 && python3 -c"import os.path;print(os.path.realpath('$(type -p python3)'))" && python3 --version
276-
echo
277-
278-
if [ -e /usr/bin/python ]; then
279-
echo "/usr/bin/python: sys.executable: $(/usr/bin/python -c 'import sys; print(sys.executable)')"
280-
fi
281-
282-
if [ -e /usr/bin/python2 ]; then
283-
echo "/usr/bin/python2: sys.executable: $(/usr/bin/python2 -c 'import sys; print(sys.executable)')"
284-
fi
285-
286-
if [ -e /usr/bin/python2.7 ]; then
287-
echo "/usr/bin/python2.7: sys.executable: $(/usr/bin/python2.7 -c 'import sys; print(sys.executable)')"
288-
fi
289-
290-
if [ -e /Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 ]; then
291-
# GitHub macOS 12 images: python2.7 is installed, but not on $PATH
292-
echo "/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7: sys.executable: $(/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7 -c 'import sys; print(sys.executable)')"
293-
fi
179+
- run: .ci/show_python_versions
294180
- run: .ci/install_sshpass ${{ matrix.sshpass_version }}
295181
if: ${{ matrix.sshpass_version }}
296-
- name: Install tooling
182+
- name: Install deps
183+
id: install-deps
297184
run: |
298185
set -o errexit -o nounset -o pipefail
299-
300-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
301-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
302-
303-
if [[ -z $PYTHON ]]; then
304-
echo 1>&2 "Python interpreter could not be determined"
305-
exit 1
306-
fi
186+
source .ci/bash_functions
187+
PYTHON="$(toxenv-python '${{ matrix.tox_env }}')"
307188
308189
"$PYTHON" -m pip install -r "tests/requirements-tox.txt"
190+
echo "python=$PYTHON" >> $GITHUB_OUTPUT
309191
- name: Run tests
310192
env:
311193
GITHUB_ACTOR: ${{ github.actor }}
312194
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
313195
run: |
314196
set -o errexit -o nounset -o pipefail
315-
316-
# Tox environment name (e.g. py312-m_mtg) -> Python executable name (e.g. python3.12)
317-
PYTHON=$(python -c 'import re; print(re.sub(r"^py([23])([0-9]{1,2}).*", r"python\1.\2", "${{ matrix.tox_env }}"))')
318-
319-
if [[ -z $PYTHON ]]; then
320-
echo 1>&2 "Python interpreter could not be determined"
321-
exit 1
322-
fi
197+
PYTHON="${{ steps.install-deps.outputs.python }}"
323198
324199
"$PYTHON" -m tox -e "${{ matrix.tox_env }}"
325200

ansible_mitogen/module_finder.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
# Python < 3.4, PEP 302 Import Hooks
4545
import imp
4646

47-
import mitogen.master
47+
import mitogen.imports
4848

4949

5050
LOG = logging.getLogger(__name__)
@@ -146,7 +146,7 @@ def scan_fromlist(code):
146146
>>> list(scan_fromlist(code))
147147
[(0, 'a'), (0, 'b.c'), (0, 'd.e.f'), (0, 'g.h'), (0, 'g.i')]
148148
"""
149-
for level, modname_s, fromlist in mitogen.master.scan_code_imports(code):
149+
for level, modname_s, fromlist in mitogen.imports.codeobj_imports(code):
150150
for name in fromlist:
151151
yield level, str('%s.%s' % (modname_s, name))
152152
if not fromlist:
@@ -172,7 +172,7 @@ def walk_imports(code, prefix=None):
172172
prefix = ''
173173
pattern = re.compile(r'(^|\.)(\w+)')
174174
start = len(prefix)
175-
for _, name, fromlist in mitogen.master.scan_code_imports(code):
175+
for _, name, fromlist in mitogen.imports.codeobj_imports(code):
176176
if not name.startswith(prefix):
177177
continue
178178
for match in pattern.finditer(name, start):

docs/changelog.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ To avail of fixes in an unreleased version, please download a ZIP file
1818
`directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
1919

2020

21+
v0.3.27 (2025-08-20)
22+
--------------------
23+
24+
* :gh:issue:`1325` :mod:`mitogen`: Refactor
25+
``mitogen.master.scan_code_imports()`` as
26+
:func:`mitogen.import.codeobj_imports` and speed-up by 1.5 - 2.5 x
27+
* :gh:issue:`1329` CI: Refactor and de-duplicate Github Actions workflow
28+
* :gh:issue:`1315` CI: macOS: Increase failed logins limit of test users
29+
* :gh:issue:`1325` tests: Improve ``master_test.ScanCodeImportsTest`` coverage
30+
31+
2132
v0.3.26 (2025-08-04)
2233
--------------------
2334

mitogen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636

3737
#: Library version as a tuple.
38-
__version__ = (0, 3, 26)
38+
__version__ = (0, 3, 27)
3939

4040

4141
#: This is :data:`False` in slave contexts. Previously it was used to prevent

mitogen/core.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,7 @@ class Importer(object):
13001300
'kubectl',
13011301
'fakessh',
13021302
'fork',
1303+
'imports',
13031304
'jail',
13041305
'lxc',
13051306
'lxd',

mitogen/imports/__init__.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-FileCopyrightText: 2025 Mitogen authors <https://github.com/mitogen-hq>
2+
# SPDX-License-Identifier: MIT
3+
# !mitogen: minify_safe
4+
5+
import sys
6+
7+
if sys.version_info >= (3, 6):
8+
from mitogen.imports._py36 import _code_imports
9+
elif sys.version_info >= (2, 5):
10+
from mitogen.imports._py2 import _code_imports_py25 as _code_imports
11+
else:
12+
from mitogen.imports._py2 import _code_imports_py24 as _code_imports
13+
14+
15+
def codeobj_imports(co):
16+
"""
17+
Yield (level, modname, names) tuples by scanning the code object `co`.
18+
19+
Top level `import mod` & `from mod import foo` statements are matched.
20+
Those inside a `class ...` or `def ...` block are currently skipped.
21+
22+
>>> co = compile('import a, b; from c import d, e as f', '<str>', 'exec')
23+
>>> list(codeobj_imports(co)) # doctest: +ELLIPSIS
24+
[(..., 'a', ()), (..., 'b', ()), (..., 'c', ('d', 'e'))]
25+
26+
:return:
27+
Generator producing `(level, modname, names)` tuples, where:
28+
29+
* `level`:
30+
-1 implicit relative (Python 2.x default)
31+
0 absolute (Python 3.x, `from __future__ import absolute_import`)
32+
>0 explicit relative (`from . import a`, `from ..b, import c`)
33+
* `modname`: Name of module to import, or to import `names` from.
34+
* `names`: tuple of names in `from mod import ..`.
35+
"""
36+
return _code_imports(co.co_code, co.co_consts, co.co_names)

0 commit comments

Comments
 (0)