facts.server.Mounts broken on 3.4
Describe the bug
- Calling the
facts.server.Mountsfact from thefactoperation results in theAttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module'exception. - Calling the
facts.server.Mountsfact from a deployment file results in theValueError: not enough values to unpack (expected 2, got 1)exception.
To Reproduce
-
AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module': console:$ pyinfra --limit control-r2 inventory.py fact server.Mounts ... AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module' -
ValueError: not enough values to unpack (expected 2, got 1)deployments/appjail.py (excerpt):if host.data.get("tmpfs"): files.directory( name="Create directory for temporary directory", path="/usr/local/appjail/cache/tmp/.appjail" ) files.line( name="Configure fstab to mount the in-memory temporary directory", line="tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0", path="/etc/fstab" ) server.mount( name="Mount tmpfs as the temporary directory", path="/usr/local/appjail/cache/tmp/.appjail", options=["rw"], device="tmpfs", fs_type="tmpfs" )console:
$ pyinfra -y --limit control-r2 inventory.py deployments/appjail.py ... ValueError: not enough values to unpack (expected 2, got 1)
Meta
- Include output of
pyinfra --support:/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/pkey.py:82: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "cipher": algorithms.TripleDES, /home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/transport.py:253: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "class": algorithms.TripleDES, If you are having issues with pyinfra or wish to make feature requests, please check out the GitHub issues at https://github.com/Fizzadar/pyinfra/issues . When adding an issue, be sure to include the following: System: FreeBSD Platform: FreeBSD-14.3-RELEASE-amd64-64bit-ELF Release: 14.3-RELEASE Machine: amd64 pyinfra: v3.4 click: v8.2.1 click: v8.2.1 click: v8.2.1 distro: v1.9.0 gevent: v25.5.1 jinja2: v3.1.6 packaging: v25.0 paramiko: v2.11.0 python-dateutil: v2.9.0.post0 pywinrm: v0.5.0 typeguard: v4.4.4 typing-extensions: v4.14.1 Executable: /home/user/Devel/pyinfra/env/bin/pyinfra Python: 3.11.13 (CPython, Clang 18.1.6 (https://github.com/llvm/llvm-project.git llvmorg-18.1.6-0-g1118c2) - How was pyinfra installed (source/pip):
virtualenv env && . env/bin/activate && pip install -e . - Include pyinfra-debug.log (if one was created)
ValueError: not enough values to unpack (expected 2, got 1):
File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 225, in cli _main(*args, **kwargs) File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 394, in _main run_ops(state, serial=serial, no_wait=no_wait) File "/home/user/Devel/pyinfra/pyinfra/api/operations.py", line 334, in run_ops _run_single_op(state, op_hash) File "/home/user/Devel/pyinfra/pyinfra/api/operations.py", line 302, in _run_single_op if not greenlet.get(): ^^^^^^^^^^^^^^ File "src/gevent/greenlet.py", line 797, in gevent._gevent_cgreenlet.Greenlet.get File "src/gevent/greenlet.py", line 373, in gevent._gevent_cgreenlet.Greenlet._raise_exception File "/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/gevent/_compat.py", line 51, in reraise raise value.with_traceback(tb) File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run File "/home/user/Devel/pyinfra/pyinfra/api/operations.py", line 184, in _run_host_op_with_context return run_host_op(state, host, op_hash) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/operations.py", line 53, in run_host_op return _run_host_op(state, host, op_hash) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/operations.py", line 81, in _run_host_op for command in op_data.command_generator(): ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/operation.py", line 283, in command_generator for command in func(*args, **kwargs): ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/operations/server.py", line 314, in mount mounts = host.get_fact(Mounts) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/host.py", line 367, in get_fact return get_fact(self.state, self, name_or_cls, args=args, kwargs=kwargs) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 185, in get_fact return _get_fact( ^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 269, in _get_fact data = fact.process(stdout_lines) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/facts/server.py", line 253, in process optional, line = line.split(sep=" ", maxsplit=1) ^^^^^^^^^^^^^^^^^ ValueError: not enough values to unpack (expected 2, got 1)AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module':
File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 225, in cli _main(*args, **kwargs) File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 354, in _main can_diff, state, config = _handle_commands( ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 656, in _handle_commands state, fact_data = _run_fact_operations(state, config, operations) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 697, in _run_fact_operations fact_data[fact_key] = get_facts( ^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 159, in get_facts results[host] = greenlet.get() ^^^^^^^^^^^^^^ File "src/gevent/greenlet.py", line 797, in gevent._gevent_cgreenlet.Greenlet.get File "src/gevent/greenlet.py", line 373, in gevent._gevent_cgreenlet.Greenlet._raise_exception File "/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/gevent/_compat.py", line 51, in reraise raise value.with_traceback(tb) File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 147, in get_host_fact return get_fact(state, host, *args, **kwargs) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 185, in get_fact return _get_fact( ^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 228, in _get_fact command = _make_command(fact.command, fact_kwargs) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 118, in _make_command return command_attribute(**host_args) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/facts/server.py", line 211, in command self._kernel = host.get_fact(Kernel) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 53, in __getattr__ if self._get_module() is None: ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 35, in _get_module return self._container.module ^^^^^^^^^^^^^^^^^ File "src/gevent/local.py", line 410, in gevent._gevent_clocal.local.__getattribute__ AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module' - Consider including output with
-vvand--debugValueError: not enough values to unpack (expected 2, got 1):/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/pkey.py:82: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "cipher": algorithms.TripleDES, /home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/transport.py:253: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "class": algorithms.TripleDES, --> Loading config... --> Loading inventory... [pyinfra_cli.inventory] Creating fake inventory... [pyinfra_cli.inventory] Checking possible group_data at: /home/user/Automation.pyinfra/group_data [pyinfra_cli.inventory] Looking for group data in: /home/user/Automation.pyinfra/group_data/all.py [pyinfra_cli.inventory] Looking for group data in: /home/user/Automation.pyinfra/group_data/vm.py [pyinfra_cli.inventory] Adding data to group all: {'promtail_conf': 'templates/promtail.yaml.j2', 'dnsmasq_conf': 'files/dnsmasq.conf', 'tmpfs': False, 'pf_conf': 'files/pf.conf', 'appjail_conf': 'templates/appjail.conf.j2', 'appjail_resolv_conf': 'files/appjail-resolv.conf', 'enable_zfs': False, 'enable_debug': False, 'freebsd_version': '14.3', 'overlord_conf': 'files/overlord.yml', '_get_pty': True} [pyinfra_cli.inventory] Adding data to group vm: {'ext_if': 'vtnet0', 'tmpfs': True} --> Connecting to hosts... [pyinfra.connectors.ssh] Connecting to: control-r2 ({'allow_agent': True, 'look_for_keys': True, '_pyinfra_ssh_forward_agent': False, '_pyinfra_ssh_config_file': None, '_pyinfra_ssh_known_hosts_file': None, '_pyinfra_ssh_strict_host_key_checking': 'accept-new', '_pyinfra_ssh_paramiko_connect_kwargs': None, 'timeout': 10}) [pyinfra.connectors.sshuserclient.client] Loading SSH config: None [control-r2] Connected [pyinfra.api.state] Activating host: control-r2 --> Preparing operation files... Loading: deployments/appjail.py [pyinfra.api.operation] Adding operation, {'Create directory for temporary directory'}, opOrder=(0, 10), opHash=3b2f90227d43a40a2288506c2a99bdc5e71f32b2 [pyinfra.api.facts] Getting fact: files.Directory (path=/usr/local/appjail/cache/tmp/.appjail) (ensure_hosts: None) [pyinfra.connectors.ssh] Running command on control-r2: (pty=True) sh -c '! (test -e /usr/local/appjail/cache/tmp/.appjail || test -L /usr/local/appjail/cache/tmp/.appjail ) || ( stat -c '"'"'user=%U group=%G mode=%A atime=%X mtime=%Y ctime=%Z size=%s %N'"'"' /usr/local/appjail/cache/tmp/.appjail 2> /dev/null || stat -f '"'"'user=%Su group=%Sg mode=%Sp atime=%a mtime=%m ctime=%c size=%z %N%SY'"'"' /usr/local/appjail/cache/tmp/.appjail )' [control-r2] >>> sh -c '! (test -e /usr/local/appjail/cache/tmp/.appjail || test -L /usr/local/appjail/cache/tmp/.appjail ) || ( stat -c '"'"'user=%U group=%G mode=%A atime=%X mtime=%Y ctime=%Z size=%s %N'"'"' /usr/local/appjail/cache/tmp/.appjail 2> /dev/null || stat -f '"'"'user=%Su group=%Sg mode=%Sp atime=%a mtime=%m ctime=%c size=%z %N%SY'"'"' /usr/local/appjail/cache/tmp/.appjail )' [pyinfra.connectors.ssh] Waiting for exit status... [pyinfra.connectors.ssh] Command exit status: 0 [control-r2] Loaded fact files.Directory (path=/usr/local/appjail/cache/tmp/.appjail) [control-r2] noop: directory /usr/local/appjail/cache/tmp/.appjail already exists [pyinfra.api.operation] Adding operation, {'Configure fstab to mount the in-memory temporary directory'}, opOrder=(0, 15), opHash=51f6b23b4aa3365d626750a07950b82d1a301941 [pyinfra.api.facts] Getting fact: files.FindInFile (interpolate_variables=False, path=/etc/fstab, pattern=^.*tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0.*$) (ensure_hosts: None) [pyinfra.connectors.ssh] Running command on control-r2: (pty=True) sh -c 'grep -e '"'"'^.*tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0.*$'"'"' /etc/fstab 2> /dev/null || ( find /etc/fstab -type f > /dev/null && echo __pyinfra_exists_/etc/fstab || true )' [control-r2] >>> sh -c 'grep -e '"'"'^.*tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0.*$'"'"' /etc/fstab 2> /dev/null || ( find /etc/fstab -type f > /dev/null && echo __pyinfra_exists_/etc/fstab || true )' [pyinfra.connectors.ssh] Waiting for exit status... [pyinfra.connectors.ssh] Command exit status: 0 [control-r2] Loaded fact files.FindInFile (interpolate_variables=False, path=/etc/fstab, pattern=^.*tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0.*$) [control-r2] noop: line "tmpfs /usr/local/appjail/cache/tmp/.appjail tmpfs rw,late 0 0" exists in /etc/fstab [pyinfra.api.operation] Adding operation, {'Mount tmpfs as the temporary directory'}, opOrder=(0, 21), opHash=b1d1da4808a9765a47e35b9cfb13e271e108f5f0 [pyinfra.api.facts] Getting fact: server.Mounts () (ensure_hosts: None) [pyinfra.api.facts] Getting fact: server.Kernel () (ensure_hosts: None) [pyinfra.connectors.ssh] Running command on control-r2: (pty=True) sh -c 'uname -s' [control-r2] >>> sh -c 'uname -s' [pyinfra.connectors.ssh] Waiting for exit status... [pyinfra.connectors.ssh] Command exit status: 0 [control-r2] Loaded fact server.Kernel [pyinfra.connectors.ssh] Running command on control-r2: (pty=True) sh -c 'mount -p --libxo json' [control-r2] >>> sh -c 'mount -p --libxo json' [pyinfra.connectors.ssh] Waiting for exit status... [pyinfra.connectors.ssh] Command exit status: 0 --> Disconnecting from hosts... --> An exception occurred in: deployments/appjail.py: Traceback (most recent call last): File "/home/user/Devel/pyinfra/pyinfra_cli/util.py", line 65, in exec_file exec(PYTHON_CODES[filename], data) File "deployments/appjail.py", line 21, in <module> server.mount( File "/home/user/Devel/pyinfra/pyinfra/api/operation.py", line 296, in decorated_func for _ in command_generator(): File "/home/user/Devel/pyinfra/pyinfra/api/operation.py", line 283, in command_generator for command in func(*args, **kwargs): File "/home/user/Devel/pyinfra/pyinfra/operations/server.py", line 314, in mount mounts = host.get_fact(Mounts) ^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/host.py", line 367, in get_fact return get_fact(self.state, self, name_or_cls, args=args, kwargs=kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 185, in get_fact return _get_fact( ^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 269, in _get_fact data = fact.process(stdout_lines) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/facts/server.py", line 253, in process optional, line = line.split(sep=" ", maxsplit=1) ^^^^^^^^^^^^^^ ValueError: not enough values to unpack (expected 2, got 1)AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module':/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/pkey.py:82: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "cipher": algorithms.TripleDES, /home/user/Devel/pyinfra/env/lib/python3.11/site-packages/paramiko/transport.py:253: CryptographyDeprecationWarning: TripleDES has been moved to cryptography.hazmat.decrepit.ciphers.algorithms.TripleDES and will be removed from cryptography.hazmat.primitives.ciphers.algorithms in 48.0.0. "class": algorithms.TripleDES, --> Loading config... --> Loading inventory... [pyinfra_cli.inventory] Creating fake inventory... [pyinfra_cli.inventory] Checking possible group_data at: /home/user/Automation.pyinfra/group_data [pyinfra_cli.inventory] Looking for group data in: /home/user/Automation.pyinfra/group_data/all.py [pyinfra_cli.inventory] Looking for group data in: /home/user/Automation.pyinfra/group_data/vm.py [pyinfra_cli.inventory] Adding data to group all: {'promtail_conf': 'templates/promtail.yaml.j2', 'dnsmasq_conf': 'files/dnsmasq.conf', 'tmpfs': False, 'pf_conf': 'files/pf.conf', 'appjail_conf': 'templates/appjail.conf.j2', 'appjail_resolv_conf': 'files/appjail-resolv.conf', 'enable_zfs': False, 'enable_debug': False, 'freebsd_version': '14.3', 'overlord_conf': 'files/overlord.yml', '_get_pty': True} [pyinfra_cli.inventory] Adding data to group vm: {'ext_if': 'vtnet0', 'tmpfs': True} --> Connecting to hosts... [pyinfra.connectors.ssh] Connecting to: control-r2 ({'allow_agent': True, 'look_for_keys': True, '_pyinfra_ssh_forward_agent': False, '_pyinfra_ssh_config_file': None, '_pyinfra_ssh_known_hosts_file': None, '_pyinfra_ssh_strict_host_key_checking': 'accept-new', '_pyinfra_ssh_paramiko_connect_kwargs': None, 'timeout': 10}) [pyinfra.connectors.sshuserclient.client] Loading SSH config: None [control-r2] Connected [pyinfra.api.state] Activating host: control-r2 --> Gathering facts... [pyinfra.api.facts] Getting fact: server.Mounts () (ensure_hosts: None) Traceback (most recent call last): File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 147, in get_host_fact return get_fact(state, host, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 185, in get_fact return _get_fact( ^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 228, in _get_fact command = _make_command(fact.command, fact_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 118, in _make_command return command_attribute(**host_args) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/facts/server.py", line 211, in command self._kernel = host.get_fact(Kernel) ^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 53, in __getattr__ if self._get_module() is None: ^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 35, in _get_module return self._container.module ^^^^^^^^^^^^^^^^^^^^^^ File "src/gevent/local.py", line 410, in gevent._gevent_clocal.local.__getattribute__ AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module' 2025-07-12T20:35:13Z <Greenlet at 0x2e43b58b04a0: get_host_fact(Host(control-r2), <class 'pyinfra.facts.server.Mounts'>, args=(), kwargs={}, apply_failed_hosts=False)> failed with AttributeError --> Disconnecting from hosts... --> An internal exception occurred: File "src/gevent/local.py", line 410, in gevent._gevent_clocal.local.__getattribute__ AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module' [pyinfra_cli.exceptions] File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 225, in cli _main(*args, **kwargs) File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 354, in _main can_diff, state, config = _handle_commands( ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 656, in _handle_commands state, fact_data = _run_fact_operations(state, config, operations) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra_cli/main.py", line 697, in _run_fact_operations fact_data[fact_key] = get_facts( ^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 159, in get_facts results[host] = greenlet.get() ^^^^^^^^^^^^^^ File "src/gevent/greenlet.py", line 797, in gevent._gevent_cgreenlet.Greenlet.get File "src/gevent/greenlet.py", line 373, in gevent._gevent_cgreenlet.Greenlet._raise_exception File "/home/user/Devel/pyinfra/env/lib/python3.11/site-packages/gevent/_compat.py", line 51, in reraise raise value.with_traceback(tb) File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 147, in get_host_fact return get_fact(state, host, *args, **kwargs) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 185, in get_fact return _get_fact( ^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 228, in _get_fact command = _make_command(fact.command, fact_kwargs) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/api/facts.py", line 118, in _make_command return command_attribute(**host_args) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/facts/server.py", line 211, in command self._kernel = host.get_fact(Kernel) ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 53, in __getattr__ if self._get_module() is None: ^^^^^^^^^^^^^^^^^ File "/home/user/Devel/pyinfra/pyinfra/context.py", line 35, in _get_module return self._container.module ^^^^^^^^^^^^^^^^^ File "src/gevent/local.py", line 410, in gevent._gevent_clocal.local.__getattribute__ [pyinfra_cli.exceptions] AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module' --> The full traceback has been written to pyinfra-debug.log --> If this is unexpected please consider submitting a bug report on GitHub, for more information run `pyinfra --support`.
I believe this should be fixed by 3.4.1 (fix: https://github.com/pyinfra-dev/pyinfra/commit/8e2335415d6a48ce05dbbe086f932cec4648c61f)
I believe this should be fixed by 3.4.1 (fix: 8e23354)
Tested again at that branch, but the same error occurred.
Can you reproduce the issue?
I can reproduce this issue by running pyinfra @local fact server.Mounts (Fedora 42, Python 3.13.7, pyinfra @ eb0e683):
Traceback (most recent call last):
File "src/gevent/greenlet.py", line 900, in gevent._gevent_cgreenlet.Greenlet.run
File "/data/git/pyinfra/pyinfra/api/facts.py", line 147, in get_host_fact
return get_fact(state, host, *args, **kwargs)
File "/data/git/pyinfra/pyinfra/api/facts.py", line 203, in get_fact
return _get_fact(
state,
...<5 lines>...
apply_failed_hosts,
)
File "/data/git/pyinfra/pyinfra/api/facts.py", line 246, in _get_fact
command = _make_command(fact.command, fact_kwargs)
File "/data/git/pyinfra/pyinfra/api/facts.py", line 118, in _make_command
return command_attribute(**host_args)
File "/data/git/pyinfra/pyinfra/facts/server.py", line 211, in command
self._kernel = host.get_fact(Kernel)
^^^^^^^^^^^^^
File "/data/git/pyinfra/pyinfra/context.py", line 53, in __getattr__
if self._get_module() is None:
~~~~~~~~~~~~~~~~^^
File "/data/git/pyinfra/pyinfra/context.py", line 35, in _get_module
return self._container.module
^^^^^^^^^^^^^^^^^^^^^^
File "src/gevent/local.py", line 410, in gevent._gevent_clocal.local.__getattribute__
AttributeError: 'gevent._gevent_clocal.local' object has no attribute 'module'
2025-09-13T20:37:59Z <Greenlet at 0x7ff9d3f3c360: get_host_fact(Host(@local), <class 'pyinfra.facts.server.Mounts'>, args=(), kwargs={}, apply_failed_hosts=False)> failed with AttributeError
Found it! It's due to the change in 3.4 to allow facts to call each other, but the context was never correctly set in the facts API, https://github.com/pyinfra-dev/pyinfra/commit/0c48515f324b1b73b5c5fd6df5291732be989baf will fix.