Commit Graph

173 Commits (8d1b01d8efba5adcc996c93700826377c3d8e21f)

Author SHA1 Message Date
David Wilson 8d1b01d8ef Refactor Stream, introduce quasi-asynchronous connect, much more
Split Stream into many, many classes

  * mitogen.parent.Connection: Handles connection setup logic only.
    * Maintain references to stdout and stderr streams.
    * Manages TimerList timer to cancel connection attempt after
      deadline
    * Blocking setup code replaced by async equivalents running on the
      broker

  * mitogen.parent.Options: Tracks connection-specific options. This
    keeps the connection class small, but more importantly, it is
    generic to the future desire to build and execute command lines
    without starting a full connection.

  * mitogen.core.Protocol: Handles program behaviour relating to events
    on a stream. Protocol performs no IO of its own, instead deferring
    it to Stream and Side. This makes testing much easier, and means
    libssh can reimplement Stream and Side to reuse MitogenProtocol

  * mitogen.core.MitogenProtocol: Guts of the old Mitogen stream
    implementtion

  * mitogen.core.BufferedWriter: Guts of the old Mitogen buffered
    transmit implementation, made generic

  * mitogen.core.DelineatedProtocol: Guts of the old IoLogger, knows how
    to split up input and pass it on to a
    on_line_received()/on_partial_line_received() callback.

  * mitogen.parent.BootstrapProtocol: Asynchronous equivalent of the old
    blocking connect code. Waits for various prompts (MITO001 etc) and
    writes the bootstrap using a BufferedWriter. On success, switches
    the stream to MitogenProtocol.

  * mitogen.core.Message: move encoding parts of MitogenProtocol out to
    Message (where it belongs) and write a bunch of new tests for
    pickling.

  * The bizarre Stream.construct() is gone now, Option.__init__ is its
    own constructor. Should fix many LGTM errors.

* Update all connection methods:  Every connection method is updated to
  use async logic, defining protocols as required to handle interactive
  prompts like in SSH or su. Add new real integration tests for at least
  doas and su.

* Eliminate manual fd management: File descriptors are trapped in file
  objects at their point of origin, and Side is updated to use file
  objects rather than raw descriptors. This eliminates a whole class of
  bugs where unrelated FDs could be closed by the wrong component. Now
  an FD's open/closed status is fused to it everywhere in the library.

* Halve file descriptor usage: now FD open/close state is tracked by
  its file object, we don't need to duplicate FDs everywhere so that
  receive/transmit side can be closed independently. Instead both sides
  back on to the same file object. Closes #26, Closes #470.

* Remove most uses of dup/dup2: Closes #256. File descriptors are
  trapped in a common file object and shared among classes. The
  remaining few uses for dup/dup2 are as close to minimal as possible.

* Introduce mitogen.parent.Process: uniform interface for subprocesses
  created either via mitogen.fork or the subprocess module. Remove all
  the crap where we steal a pid from subprocess guts. Now we use
  subprocess to manage its processes as it should be. Closes #169 by
  using the new Timers facility to poll for a slow-to-exit subprocess.

* Fix su password race: Closes #363. DelineatedProtocol naturally
  retries partially received lines, preventing the cause of the original
  race.

* Delete old blocking IO utility functions
  iter_read()/write_all()/discard_until().

Closes #26
Closes #147
Closes #169
Closes #256
Closes #363
Closes #419
Closes #470
5 years ago
David Wilson 46ebd56c7a core/master: docstring, repr, and debug log message cleanups
Debug output is vastly more readable now.
5 years ago
David Wilson 3f1ef6e243 master: expect forwarded logs to be in UTF-8.
latin1 was causing corruption of internationalized messages.
5 years ago
David Wilson 2fbc77a155 issue #170: implement timers. 5 years ago
David Wilson ed8acb5153 master: sysconfig did not exist until 2.7. 5 years ago
David Wilson 5eb10aacef master: fix _is_stdlib_path() failure on Ubuntu. 5 years ago
David Wilson 72ab917c89 issue #590: add FinderMethod docstrings. 5 years ago
David Wilson 875ff5c060 issue #590: refactor ModuleFinder and teach it a new special case.
Now it's possible to find both packages and modules when the
sys.modules[...] state for the package/module is junk. Previously only
modules were possible.

This also refactors things to make writing better tests for all these
cases much simpler.
5 years ago
David Wilson 1f77d24bec Update copyright year everywhere. 5 years ago
David Wilson 7ff4e6694c issue #536: rework how 2.3-compatible simplejson is served
Regardless of the version of simplejson loaded in the master, load up
the ModuleResponder cache with our 2.4-compatible version.

To cope with simplejson being loaded due to modules like ec2_group that
try to import it before importing 'json', also update target.py to
remove it from the whitelist if a local 'json' module import succeeds.
5 years ago
David Wilson b263e01867 issue #481: avoid crash if disconnect occurs during forward_modules() 5 years ago
David Wilson 85cfa3b0f5 master: .encode() needed for Py3. 5 years ago
David Wilson 1d509d03ff issue #508: master: minify_safe_re must be bytes for Py3. 5 years ago
David Wilson 0e193c223c issue #508: master: minify all Mitogen/ansible_mitogen sources.
Minify-safe files are marked with a magical "# !mitogen: minify_safe"
comment anywhere in the file, which activates the minifier. The result
is naturally cached by ModuleResponder, therefore lru_cache is gone too.

Given:

    import os, mitogen
    @mitogen.main()
    def main(router):
        c = router.ssh(hostname='k3')
        c.call(os.getpid)
        router.sudo(via=c)

SSH footprint drops from 56.2 KiB to 42.75 KiB (-23.9%)
Ansible "shell: hostname" drops 149.26 KiB to 117.42 KiB (-21.3%)
5 years ago
David Wilson 9adc38d8ec parent: pre-cache bootstrap if possible.
When the interpreter is modern enough, use zlib.compressobj() to
pre-compress the unchanging parts of the bootstrap once, then use
compressobj.copy() to append just the context's config during stream
construction.

Before: 100 loops, best of 3: 5.81 msec per loop
After: 10000 loops, best of 3: 35.9 usec per loop

With 100 targets this is enough to knock 6 seconds off startup, at 500
targets it becomes half a minute.

Test 'program':
        python -m timeit -s '
                import mitogen.parent as p;
                import mitogen.master as m;
                r=m.Router();
                s=p.Stream(r, 0, max_message_size=1);
                r.broker.shutdown()'\
                \
                's.get_preamble()'
5 years ago
David Wilson cafdfdb8e2 master: set Router.profiling if MITOGEN_PROFILING variable present. 5 years ago
David Wilson b8a0e0f929 master: cache sent/forwarded module names
On 32x Docker run of issue_140__thread_pileup.yml

Before:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   384807    3.595    0.000   12.207    0.000 /home/dmw/src/mitogen/mitogen/master.py:896(_forward_one_module)
  1218352    4.867    0.000    7.302    0.000 /home/dmw/src/mitogen/mitogen/master.py:853(_send_module_and_related)

After:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   384807    3.839    0.000    6.543    0.000 /home/dmw/src/mitogen/mitogen/master.py:902(_forward_one_module)
  1218352    0.723    0.000    0.898    0.000 /home/dmw/src/mitogen/mitogen/master.py:856(_send_module_and_related)
5 years ago
David Wilson dc4b27c6bf master: keep is_stdlib_path() result as negative cache entry
On 32x Docker run of issue_140__thread_pileup.yml

Before:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  1218500    1.716    0.000    7.325    0.000 /home/dmw/src/mitogen/mitogen/master.py:118(is_stdlib_path)

After:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      166    0.000    0.000    0.001    0.000 /home/dmw/src/mitogen/mitogen/master.py:123(is_stdlib_path)
5 years ago
David Wilson 3435f24e8d issue #479: ModuleFinder special case for __main__ on Py3.x. 5 years ago
David Wilson 4ca3051e39 issue #477: document master.Router.max_message_size. 5 years ago
David Wilson 19eafc5755 issue #477: set().union(a, b, ..) unsupported on Py2.4. 5 years ago
David Wilson e460d648d5 issue #477: Logger.log(extra=) unsupported on Py2.4. 5 years ago
David Wilson 112caa94f9 issue #477: Py2.4 dep scanner bytecode difference 5 years ago
David Wilson 7ecd5d8ba3 issue #477: ModuleFinder test fixes. 5 years ago
David Wilson bc434a4f99 issue #477: backport mitogen.master to Python 2.4. 5 years ago
David Wilson 97a96f5dd8 issue #477: rename and add tests for polyfill functions. 5 years ago
David Wilson 5135ff9068 issue #477: master: ability to override ModuleResponder output.
This is needed to cope Ansible 2.3 doing weird stuff as usual. It serves
up __init__.py for ansible and ansible.module_utils as hard-coded
namespace packages, the real ansible/__init__.py on disk is not 2.4
compatible.
5 years ago
David Wilson 96c35ccab1 issue #61: unused variable (reported by LGTM) 5 years ago
David Wilson 06415bb720 issue #310: fix test failures, teach old import method new tricks
- don't try anything unless something really lives in sys.modules by
  that name
- non-ASCII files are possible
- the unimportable thing might be an extension module, we don't want
  that
5 years ago
David Wilson 6af1a64cce master: handle crazy non-modules in sys.modules again; closes #310. 5 years ago
David Wilson fd90834944 issue #408: fix test fallout. 5 years ago
David Wilson 41626b82dd issue #408: 2.4 compat: remove ternary if use in master.py. 5 years ago
David Wilson cce1dbf3b1 tests: quieten a bunch of spam printed during run 5 years ago
David Wilson b6840aab75 issue #459: one line stats output during shutdown
CI logs are too noisy.
5 years ago
David Wilson f2f41809ae issue #459: initial get_stats() implementation 5 years ago
David Wilson d030decf57 issue #444: master: lower log level for soft import error. 6 years ago
David Wilson 8e4c164d93 issue #388: fix Sphinx markup 6 years ago
David Wilson 804bacdadb docs: move most remaining docstrings back into *.py; closes #388
The remaining ones are decorators which don't seem to have an autodoc
equivlent.
6 years ago
David Wilson 0d04e940b7 master: docstring fixes. 6 years ago
David Wilson 9828588e97 master: group is_stdlib_name() with other module functions. 6 years ago
David Wilson bf597d257f master: document LogForwarder. 6 years ago
David Wilson 74cf9c3c96 master: document ThreadWatcher 6 years ago
David Wilson 4146648759 master: log error an refuse __main__ import if no guard detected.
Closes #366.
6 years ago
David Wilson 32751cd356 master: allow batching context switches for forward_modules()
-7 switches per task.
6 years ago
David Wilson 946c4964bb issue #349: master: fix broken importer logging format string 6 years ago
David Wilson 442d88e3d7 docs: many more fixes/merges. 6 years ago
David Wilson 038f8d5dec master: fix another case where built-in module loader throws ImportError
requests/packages.py just imports urllib3 normally, then makes up new
names for it. pkgutil can't cope with that, and returns the loader
(builtin) for the requests package. The built-in loader obviously can't
find_module() for "requests/packages/urllib3/contrib/pyopenssl" because
it doesn't exist on disk.
6 years ago
David Wilson c141dd10ec master: fix resolve_relpath()
looks like this was just as broken on 2.x, and suddenly we're
finding a bunch more legit Django deps. It seems anywhere
absolute_import appeared in 2.x, we skipped some imports.
6 years ago
David Wilson b0404bef40 tests: fix get_module_via_* encoding issues 6 years ago
David Wilson 9903692811 master: update scan_code_imports to cope with wordcode
Constant-sized opcodes were introduced as an optimization in Python 3.6.
See https://bugs.python.org/issue26647
6 years ago