Skip to content

Add 3.13t and 3.14t to CI#45

Draft
clin1234 wants to merge 3 commits into
P403n1x87:mainfrom
clin1234:nogil
Draft

Add 3.13t and 3.14t to CI#45
clin1234 wants to merge 3 commits into
P403n1x87:mainfrom
clin1234:nogil

Conversation

@clin1234
Copy link
Copy Markdown

Description of the Change

This added the free-threaded stable Python interpreters for CI (currently 3.13 and 3.14)

Alternate Designs

To be filled later

Regressions

To be filled later

Verification Process

To be filled later

@clin1234
Copy link
Copy Markdown
Author

And test logs:

============================= test session starts ==============================
platform linux -- Python 3.15.0a1, pytest-9.0.0, pluggy-1.6.0
rootdir: /workspaces/austin-python
configfile: pyproject.toml
plugins: asyncio-1.3.0, cov-7.0.0
asyncio: mode=Mode.STRICT, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 56 items

test/format/test_compress.py ..                                          [  3%]
test/format/test_mojo.py ......                                          [ 14%]
test/format/test_pprof.py .                                              [ 16%]
test/format/test_speedscope.py ...                                       [ 21%]
test/stats/test_austin_file_reader.py .                                  [ 23%]
test/stats/test_austin_stats.py ....                                     [ 30%]
test/stats/test_hierarchical_stats.py ...                                [ 35%]
test/test_aio.py FFF...                                                  [ 46%]
test/test_cli.py ........                                                [ 60%]
test/test_config.py ..                                                   [ 64%]
test/test_semver.py ......                                               [ 75%]
test/test_simple.py F......                                              [ 87%]
test/test_threads.py FF...                                               [ 96%]
test/tools/test_diff.py s                                                [ 98%]
test/tools/test_resolve.py x                                             [100%]

=================================== FAILURES ===================================
_______________________________ test_async_time ________________________________

self = <test.test_aio.TestAsyncAustin object at 0x57acfe17810>

    async def on_terminate(self):
        data = self._meta
        assert "duration" in data
>       assert "errors" in data
E       AssertionError: assert 'errors' in {'austin': '4.0.0', 'count': '0', 'duration': '452', 'interval': '100', ...}

test/test_aio.py:62: AssertionError

The above exception was the direct cause of the following exception:

    @pytest.mark.asyncio
    async def test_async_time():
        austin = TestAsyncAustin()
    
        await austin.start(
            [
                "-Ci",
                "100",
                "python",
                "-c",
                "from time import sleep; sleep(2)",
            ]
        )
>       await asyncio.wait_for(austin.wait(), 30)

test/test_aio.py:90: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.15/asyncio/tasks.py:488: in wait_for
    return await fut
           ^^^^^^^^^
austin/aio.py:181: in wait
    await self._run_task
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_aio.TestAsyncAustin object at 0x57acfe17810>
mojo = <austin.format.mojo.AsyncMojoStreamReader object at 0x57acfe13190>

    async def _run(self, mojo: AsyncMojoStreamReader) -> None:
        # Start collecting samples
        self._state = AustinState.RUNNING
    
        async for e in mojo:
            if isinstance(e, AustinSample):
                try:
                    await t.cast(t.Awaitable[None], self._sample_callback(e))
                except Exception as exc:
                    raise AustinError("Error in call to sample callback") from exc
            elif isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
                if self._metadata_callback is not None:
                    try:
                        await t.cast(t.Awaitable[None], self._metadata_callback(e))
                    except Exception as exc:
                        raise AustinError("Error in call to metadata callback") from exc
    
        self._state = AustinState.TERMINATING
    
        # Call the terminate callback
        try:
            if self._terminate_callback is not None:
                await t.cast(t.Awaitable[None], self._terminate_callback())
        except Exception as exc:
>           raise AustinError("Error in call to terminate callback") from exc
E           austin.errors.AustinError: Error in call to terminate callback

austin/aio.py:109: AustinError
______________________________ test_async_memory _______________________________

self = <test.test_aio.TestAsyncAustin object at 0x57ad10b3ad0>

    async def on_terminate(self):
        data = self._meta
        assert "duration" in data
>       assert "errors" in data
E       AssertionError: assert 'errors' in {'austin': '4.0.0', 'count': '0', 'duration': '502', 'interval': '100', ...}

test/test_aio.py:62: AssertionError

The above exception was the direct cause of the following exception:

    @pytest.mark.asyncio
    async def test_async_memory():
        austin = TestAsyncAustin()
    
        async def sample_callback(data):
            austin._sample_received = True
    
        austin._sample_callback = sample_callback
        await austin.start(
            [
                "-mCi",
                "100",
                "python",
                "-c",
                "[i for i in range(10000000)]",
            ]
        )
>       await asyncio.wait_for(austin.wait(), 30)

test/test_aio.py:115: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/usr/lib/python3.15/asyncio/tasks.py:488: in wait_for
    return await fut
           ^^^^^^^^^
austin/aio.py:181: in wait
    await self._run_task
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_aio.TestAsyncAustin object at 0x57ad10b3ad0>
mojo = <austin.format.mojo.AsyncMojoStreamReader object at 0x57ad10b4ed0>

    async def _run(self, mojo: AsyncMojoStreamReader) -> None:
        # Start collecting samples
        self._state = AustinState.RUNNING
    
        async for e in mojo:
            if isinstance(e, AustinSample):
                try:
                    await t.cast(t.Awaitable[None], self._sample_callback(e))
                except Exception as exc:
                    raise AustinError("Error in call to sample callback") from exc
            elif isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
                if self._metadata_callback is not None:
                    try:
                        await t.cast(t.Awaitable[None], self._metadata_callback(e))
                    except Exception as exc:
                        raise AustinError("Error in call to metadata callback") from exc
    
        self._state = AustinState.TERMINATING
    
        # Call the terminate callback
        try:
            if self._terminate_callback is not None:
                await t.cast(t.Awaitable[None], self._terminate_callback())
        except Exception as exc:
>           raise AustinError("Error in call to terminate callback") from exc
E           austin.errors.AustinError: Error in call to terminate callback

austin/aio.py:109: AustinError
_____________________________ test_async_terminate _____________________________

    @pytest.mark.skipif(
        sys.platform == "win32", reason="Signal handling not supported on Windows"
    )
    @pytest.mark.asyncio
    async def test_async_terminate():
        austin = TestAsyncAustin()
    
        async def sample_callback(sample):
            assert sample
            if not austin._sample_received:
                austin.terminate()
            austin._sample_received = True
    
        async def terminate_callback():
            austin._terminate = True
    
        austin._sample_callback = sample_callback
        austin._terminate_callback = terminate_callback
    
>       await austin.start(["-Ci", "10ms", "python"])

test/test_aio.py:139: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_aio.TestAsyncAustin object at 0x57ad10b6050>
args = ['-Ci', '10ms', 'python']

    async def start(self, args: t.Optional[t.Sequence[str]] = None) -> None:
        """Create the start coroutine.
    
        Use with the ``asyncio`` event loop.
        """
        self._args = AustinArgumentParser().parse_args(args)
    
        self._state = AustinState.STARTING
    
        try:
            _args = list(args if args is not None else sys.argv[1:])  # Make a copy
            _args.insert(0, "-P")
            self._proc = await asyncio.create_subprocess_exec(
                self.binary_path,
                *_args,
                stdin=asyncio.subprocess.PIPE,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
        except FileNotFoundError:
            raise AustinError("Austin executable not found.") from None
    
        if not self._proc.stdout:
            raise AustinError("Standard output stream is unexpectedly missing")
        if not self._proc.stderr:
            raise AustinError("Standard error stream is unexpectedly missing")
    
        mojo = AsyncMojoStreamReader(self._proc.stdout)
    
        # Retrieve the Austin version, then call the ready callback
        async for e in mojo:
            if isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
    
                try:
                    if self._metadata_callback is not None:
                        await t.cast(t.Awaitable[None], self._metadata_callback(e))
                except Exception as exc:
                    raise AustinError("Error in call to metadata callback") from exc
    
                if e.name == "austin":
                    self._check_version()
                    break
        else:
>           raise AustinError("Cannot determine Austin version from output")
E           austin.errors.AustinError: Cannot determine Austin version from output

austin/aio.py:157: AustinError
_________________________________ test_simple __________________________________

self = <test.test_simple.TestSimpleAustin object at 0x57acfe1e410>

    def on_terminate(self):
        data = self._meta
        assert "duration" in data
>       assert "errors" in data
E       AssertionError: assert 'errors' in {'austin': '4.0.0', 'count': '0', 'duration': '465', 'interval': '1000', ...}

test/test_simple.py:57: AssertionError

The above exception was the direct cause of the following exception:

    def test_simple():
        austin = TestSimpleAustin()
    
        austin.start(["-Ci", "1000", "python", "-c", "from time import sleep; sleep(1)"])
>       austin.wait()

test/test_simple.py:74: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_simple.TestSimpleAustin object at 0x57acfe1e410>

    def wait(self) -> int:
        if self._proc is None:
            raise AustinError("Austin process is not running")
    
        self._state = AustinState.RUNNING
    
        assert self._mojo is not None
    
        for e in self._mojo:
            if isinstance(e, AustinSample):
                try:
                    self._sample_callback(e)
                except Exception as exc:
                    raise AustinError("Error in call to sample callback") from exc
            elif isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
                if self._metadata_callback is not None:
                    try:
                        self._metadata_callback(e)
                    except Exception as exc:
                        raise AustinError("Error in call to metadata callback") from exc
    
        self._state = AustinState.TERMINATING
    
        # Call the terminate callback
        try:
            if self._terminate_callback is not None:
                self._terminate_callback()
        except Exception as exc:
>           raise AustinError("Error in call to terminate callback") from exc
E           austin.errors.AustinError: Error in call to terminate callback

austin/simple.py:152: AustinError
________________________________ test_threaded _________________________________

    def test_threaded():
        austin = TestThreadedAustin()
    
        austin.start(["-i", "1000", "python", "-c", "from time import sleep; sleep(2)"])
>       assert austin.wait() == 0
               ^^^^^^^^^^^^^

test/test_threads.py:70: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
austin/threads.py:103: in wait
    self.join()
austin/threads.py:96: in join
    raise self._exc
austin/threads.py:74: in _thread_bootstrap
    super().start(args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_threads.TestThreadedAustin object at 0x57acfe1e110>
args = ['-i', '1000', 'python', '-c', 'from time import sleep; sleep(2)']

    def start(self, args: t.Optional[t.Sequence[str]] = None) -> None:
        """Start the Austin process."""
        self._args = AustinArgumentParser().parse_args(args)
    
        self._state = AustinState.STARTING
    
        try:
            self._proc = subprocess.Popen(
                [
                    str(self.binary_path),
                    "-P",
                    *(args if args is not None else sys.argv[1:]),
                ],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
        except FileNotFoundError:
            raise AustinError("Austin executable not found.") from None
    
        if not self._proc.stdout:
            raise AustinError("Standard output stream is unexpectedly missing")
        if not self._proc.stderr:
            raise AustinError("Standard error stream is unexpectedly missing")
    
        self._mojo = MojoStreamReader(self._proc.stdout)
    
        # Retrieve the Austin version, then call the ready callback
        for e in self._mojo:
            if isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
    
                try:
                    if self._metadata_callback is not None:
                        self._metadata_callback(e)
                except Exception as exc:
                    raise AustinError("Error in call to metadata callback") from exc
    
                if e.name == "austin":
                    self._check_version()
                    break
        else:
>           raise AustinError("Cannot determine Austin version from output")
E           austin.errors.AustinError: Cannot determine Austin version from output

austin/simple.py:114: AustinError
___________________________ test_threaded_terminate ____________________________

    @pytest.mark.skipif(
        sys.platform == "win32", reason="Signal handling not supported on Windows"
    )
    def test_threaded_terminate():
        austin = TestThreadedAustin()
    
        def sample_callback(*args):
            if not austin._sample_received:
                austin.terminate()
            austin._sample_received = True
    
        def terminate_callback(*args):
            austin._terminate = True
    
        austin._sample_callback = sample_callback
        austin._terminate_callback = terminate_callback
    
        austin.start(["-i", "100", "python", "-c", "from time import sleep; sleep(1)"])
    
>       assert austin.wait() != 0
               ^^^^^^^^^^^^^

test/test_threads.py:97: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
austin/threads.py:103: in wait
    self.join()
austin/threads.py:96: in join
    raise self._exc
austin/threads.py:74: in _thread_bootstrap
    super().start(args)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <test.test_threads.TestThreadedAustin object at 0x57ad10b94d0>
args = ['-i', '100', 'python', '-c', 'from time import sleep; sleep(1)']

    def start(self, args: t.Optional[t.Sequence[str]] = None) -> None:
        """Start the Austin process."""
        self._args = AustinArgumentParser().parse_args(args)
    
        self._state = AustinState.STARTING
    
        try:
            self._proc = subprocess.Popen(
                [
                    str(self.binary_path),
                    "-P",
                    *(args if args is not None else sys.argv[1:]),
                ],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
        except FileNotFoundError:
            raise AustinError("Austin executable not found.") from None
    
        if not self._proc.stdout:
            raise AustinError("Standard output stream is unexpectedly missing")
        if not self._proc.stderr:
            raise AustinError("Standard error stream is unexpectedly missing")
    
        self._mojo = MojoStreamReader(self._proc.stdout)
    
        # Retrieve the Austin version, then call the ready callback
        for e in self._mojo:
            if isinstance(e, AustinMetadata):
                self._meta[e.name] = e.value
    
                try:
                    if self._metadata_callback is not None:
                        self._metadata_callback(e)
                except Exception as exc:
                    raise AustinError("Error in call to metadata callback") from exc
    
                if e.name == "austin":
                    self._check_version()
                    break
        else:
>           raise AustinError("Cannot determine Austin version from output")
E           austin.errors.AustinError: Cannot determine Austin version from output

austin/simple.py:114: AustinError
=============================== warnings summary ===============================
venv/lib/python3.15t/site-packages/google/protobuf/internal/well_known_types.py:91
  /workspaces/austin-python/venv/lib/python3.15t/site-packages/google/protobuf/internal/well_known_types.py:91: DeprecationWarning: datetime.datetime.utcfromtimestamp() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.fromtimestamp(timestamp, datetime.UTC).
    _EPOCH_DATETIME_NAIVE = datetime.datetime.utcfromtimestamp(0)

test/format/test_mojo.py: 36 warnings
  /workspaces/austin-python/austin/format/mojo.py:97: DeprecationWarning: '_UnionGenericAlias' is deprecated and slated for removal in Python 3.17
    if isinstance(f.type, t._UnionGenericAlias)

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================================ tests coverage ================================
_______________ coverage: platform linux, python 3.15.0-alpha-1 ________________

Name                                 Stmts   Miss Branch BrPart  Cover   Missing
--------------------------------------------------------------------------------
austin/__init__.py                       0      0      0      0   100%
austin/aio.py                           76     19     26      9    71%   90-93, 94->88, 96->88, 99-100, 111, 136, 138, 144->143, 148->153, 150-151, 167-173, 177, 184
austin/base.py                          94     14     18      3    85%   52, 71-72, 151, 155, 178, 180, 224-225, 230-233, 238
austin/cli.py                           79      1     46     12    90%   101, 116->124, 124->133, 133->141, 141->149, 149->154, 154->162, 162->167, 167->176, 176->exit, 225->227, 235->238
austin/config.py                        27      5      0      0    81%   59-63
austin/errors.py                         2      0      0      0   100%
austin/events.py                        35      1      0      0    97%   102
austin/format/__init__.py                9      0      0      0   100%
austin/format/collapsed_stack.py       119     24     30      9    78%   21-22, 29-30, 45, 49-50, 66, 69, 75-76, 79, 85, 131, 156, 177-179, 192-193, 195, 234-236, 240
austin/format/compress.py               39     14      6      1    67%   77-117, 121
austin/format/mojo.py                  510     80    100     12    83%   108-109, 282-285, 458, 470, 474, 482-485, 489-492, 502, 506-507, 574, 587, 597, 602, 612, 626-632, 642, 675-687, 704->703, 721-722, 731-743, 750, 755, 758, 763, 768, 773, 778, 783, 788, 795, 809-815, 825, 842-843, 850-852, 854, 878, 879->exit, 943, 946-951, 957, 968-969
austin/format/pprof/__init__.py        110     23     24      8    74%   66->69, 70, 73, 78-81, 92-93, 153, 157-164, 166, 169-173
austin/format/pprof/__main__.py         31     31     10      0     0%   24-84
austin/format/pprof/profile_pb2.py      48      0      0      0   100%
austin/format/speedscope.py            119     32     32      4    68%   149, 153-156, 157->168, 199-250, 254
austin/simple.py                        70     17     26     10    70%   93, 95, 101->100, 105->110, 107-108, 110->100, 118-121, 125, 133-136, 137->131, 139->131, 142-143, 154-157
austin/stats.py                        130     13     40      9    85%   102, 106, 123->131, 150->159, 159->exit, 196, 225, 232->231, 236, 239, 248, 273-286, 289
austin/threads.py                       33      7      8      3    71%   75, 93, 95->exit, 101, 105-110
austin/tools/diff.py                    73     58     20      1    17%   44-52, 63-72, 87-126, 136-154, 158
austin/tools/mojodbg.py                 44     34      8      1    21%   37-60, 64-81, 85
austin/tools/resolve.py                130     92     56      1    21%   39-63, 77-103, 106-114, 117-141, 144-157, 169-193, 220-221, 228
--------------------------------------------------------------------------------
TOTAL                                 1778    465    450     83    70%
Coverage XML written to file coverage.xml
=========================== short test summary info ============================
FAILED test/test_aio.py::test_async_time - austin.errors.AustinError: Error i...
FAILED test/test_aio.py::test_async_memory - austin.errors.AustinError: Error...
FAILED test/test_aio.py::test_async_terminate - austin.errors.AustinError: Ca...
FAILED test/test_simple.py::test_simple - austin.errors.AustinError: Error in...
FAILED test/test_threads.py::test_threaded - austin.errors.AustinError: Canno...
FAILED test/test_threads.py::test_threaded_terminate - austin.errors.AustinEr...
======= 6 failed, 48 passed, 1 skipped, 1 xfailed, 37 warnings in 15.29s =======

@P403n1x87
Copy link
Copy Markdown
Owner

@clin1234 thanks for this, but note that Austin itself does not support free-threaded mode yet, so there wouldn't be much value in adding this feature here just now.

@P403n1x87
Copy link
Copy Markdown
Owner

The tests pull Austin from the devel branch, where support for FT was added recently. So once the CI config is in place the tests should pass 🤞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants