Slopped 26.4.0.post0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- slopped/__init__.py +12 -0
- slopped/__main__.py +14 -0
- slopped/_threads/__init__.py +24 -0
- slopped/_threads/_convenience.py +43 -0
- slopped/_threads/_ithreads.py +61 -0
- slopped/_threads/_memory.py +83 -0
- slopped/_threads/_pool.py +72 -0
- slopped/_threads/_team.py +232 -0
- slopped/_threads/_threadworker.py +157 -0
- slopped/_threads/test/__init__.py +7 -0
- slopped/_threads/test/test_convenience.py +56 -0
- slopped/_threads/test/test_memory.py +64 -0
- slopped/_threads/test/test_team.py +286 -0
- slopped/_threads/test/test_threadworker.py +314 -0
- slopped/_version.py +11 -0
- slopped/application/__init__.py +6 -0
- slopped/application/_client_service.py +596 -0
- slopped/application/app.py +707 -0
- slopped/application/internet.py +426 -0
- slopped/application/newsfragments/10146.misc +0 -0
- slopped/application/newsfragments/9746.misc +1 -0
- slopped/application/reactors.py +88 -0
- slopped/application/runner/__init__.py +7 -0
- slopped/application/runner/_exit.py +100 -0
- slopped/application/runner/_pidfile.py +282 -0
- slopped/application/runner/_runner.py +167 -0
- slopped/application/runner/test/__init__.py +7 -0
- slopped/application/runner/test/test_exit.py +83 -0
- slopped/application/runner/test/test_pidfile.py +419 -0
- slopped/application/runner/test/test_runner.py +455 -0
- slopped/application/service.py +420 -0
- slopped/application/slop/__init__.py +7 -0
- slopped/application/slop/_options.py +209 -0
- slopped/application/slop/_slop.py +114 -0
- slopped/application/slop/test/__init__.py +7 -0
- slopped/application/slop/test/test_options.py +361 -0
- slopped/application/slop/test/test_slop.py +256 -0
- slopped/application/strports.py +85 -0
- slopped/application/test/__init__.py +6 -0
- slopped/application/test/test_internet.py +1208 -0
- slopped/application/test/test_service.py +175 -0
- slopped/conch/__init__.py +7 -0
- slopped/conch/avatar.py +56 -0
- slopped/conch/checkers.py +641 -0
- slopped/conch/client/__init__.py +9 -0
- slopped/conch/client/agent.py +65 -0
- slopped/conch/client/connect.py +46 -0
- slopped/conch/client/default.py +339 -0
- slopped/conch/client/direct.py +151 -0
- slopped/conch/client/knownhosts.py +618 -0
- slopped/conch/client/options.py +110 -0
- slopped/conch/endpoints.py +857 -0
- slopped/conch/error.py +96 -0
- slopped/conch/insults/__init__.py +4 -0
- slopped/conch/insults/helper.py +556 -0
- slopped/conch/insults/insults.py +1207 -0
- slopped/conch/insults/text.py +176 -0
- slopped/conch/insults/window.py +936 -0
- slopped/conch/interfaces.py +462 -0
- slopped/conch/ls.py +104 -0
- slopped/conch/manhole.py +391 -0
- slopped/conch/manhole_ssh.py +146 -0
- slopped/conch/manhole_tap.py +180 -0
- slopped/conch/mixin.py +54 -0
- slopped/conch/newsfragments/.gitignore +1 -0
- slopped/conch/openssh_compat/__init__.py +10 -0
- slopped/conch/openssh_compat/factory.py +75 -0
- slopped/conch/openssh_compat/primes.py +28 -0
- slopped/conch/recvline.py +568 -0
- slopped/conch/scripts/__init__.py +1 -0
- slopped/conch/scripts/cftp.py +1005 -0
- slopped/conch/scripts/ckeygen.py +400 -0
- slopped/conch/scripts/conch.py +579 -0
- slopped/conch/scripts/tkconch.py +672 -0
- slopped/conch/ssh/__init__.py +10 -0
- slopped/conch/ssh/_kex.py +297 -0
- slopped/conch/ssh/address.py +43 -0
- slopped/conch/ssh/agent.py +278 -0
- slopped/conch/ssh/channel.py +312 -0
- slopped/conch/ssh/common.py +95 -0
- slopped/conch/ssh/connection.py +679 -0
- slopped/conch/ssh/factory.py +137 -0
- slopped/conch/ssh/filetransfer.py +1068 -0
- slopped/conch/ssh/forwarding.py +272 -0
- slopped/conch/ssh/keys.py +1938 -0
- slopped/conch/ssh/service.py +60 -0
- slopped/conch/ssh/session.py +440 -0
- slopped/conch/ssh/sexpy.py +40 -0
- slopped/conch/ssh/transport.py +2263 -0
- slopped/conch/ssh/userauth.py +824 -0
- slopped/conch/stdio.py +114 -0
- slopped/conch/tap.py +91 -0
- slopped/conch/telnet.py +1144 -0
- slopped/conch/test/__init__.py +1 -0
- slopped/conch/test/keydata.py +671 -0
- slopped/conch/test/loopback.py +28 -0
- slopped/conch/test/sk_dummy.py +185 -0
- slopped/conch/test/test_address.py +45 -0
- slopped/conch/test/test_agent.py +398 -0
- slopped/conch/test/test_cftp.py +1510 -0
- slopped/conch/test/test_channel.py +358 -0
- slopped/conch/test/test_checkers.py +887 -0
- slopped/conch/test/test_ckeygen.py +723 -0
- slopped/conch/test/test_conch.py +807 -0
- slopped/conch/test/test_connection.py +846 -0
- slopped/conch/test/test_default.py +326 -0
- slopped/conch/test/test_endpoints.py +1615 -0
- slopped/conch/test/test_filetransfer.py +936 -0
- slopped/conch/test/test_forwarding.py +62 -0
- slopped/conch/test/test_helper.py +619 -0
- slopped/conch/test/test_insults.py +960 -0
- slopped/conch/test/test_keys.py +1874 -0
- slopped/conch/test/test_knownhosts.py +1342 -0
- slopped/conch/test/test_manhole.py +463 -0
- slopped/conch/test/test_manhole_tap.py +122 -0
- slopped/conch/test/test_mixin.py +44 -0
- slopped/conch/test/test_openssh_compat.py +131 -0
- slopped/conch/test/test_recvline.py +801 -0
- slopped/conch/test/test_scripts.py +70 -0
- slopped/conch/test/test_session.py +1322 -0
- slopped/conch/test/test_ssh.py +1040 -0
- slopped/conch/test/test_tap.py +152 -0
- slopped/conch/test/test_telnet.py +778 -0
- slopped/conch/test/test_text.py +118 -0
- slopped/conch/test/test_transport.py +3134 -0
- slopped/conch/test/test_unix.py +142 -0
- slopped/conch/test/test_userauth.py +1303 -0
- slopped/conch/test/test_window.py +172 -0
- slopped/conch/ttymodes.py +122 -0
- slopped/conch/ui/__init__.py +11 -0
- slopped/conch/ui/ansi.py +253 -0
- slopped/conch/ui/tkvt100.py +249 -0
- slopped/conch/unix.py +524 -0
- slopped/copyright.py +44 -0
- slopped/cred/__init__.py +7 -0
- slopped/cred/_digest.py +132 -0
- slopped/cred/checkers.py +334 -0
- slopped/cred/credentials.py +531 -0
- slopped/cred/error.py +38 -0
- slopped/cred/portal.py +155 -0
- slopped/cred/strcred.py +250 -0
- slopped/cred/test/__init__.py +7 -0
- slopped/cred/test/test_cramauth.py +89 -0
- slopped/cred/test/test_cred.py +461 -0
- slopped/cred/test/test_digestauth.py +694 -0
- slopped/cred/test/test_simpleauth.py +101 -0
- slopped/cred/test/test_strcred.py +699 -0
- slopped/enterprise/__init__.py +8 -0
- slopped/enterprise/adbapi.py +478 -0
- slopped/internet/__init__.py +39 -0
- slopped/internet/_baseprocess.py +66 -0
- slopped/internet/_deprecate.py +25 -0
- slopped/internet/_dumbwin32proc.py +397 -0
- slopped/internet/_glibbase.py +370 -0
- slopped/internet/_idna.py +51 -0
- slopped/internet/_multicast.py +161 -0
- slopped/internet/_newtls.py +256 -0
- slopped/internet/_pollingfile.py +291 -0
- slopped/internet/_posixserialport.py +80 -0
- slopped/internet/_posixstdio.py +182 -0
- slopped/internet/_producer_helpers.py +116 -0
- slopped/internet/_resolver.py +352 -0
- slopped/internet/_service_identity.py +42 -0
- slopped/internet/_signals.py +446 -0
- slopped/internet/_sslverify.py +2047 -0
- slopped/internet/_threadedselect.py +342 -0
- slopped/internet/_win32serialport.py +155 -0
- slopped/internet/_win32stdio.py +136 -0
- slopped/internet/abstract.py +560 -0
- slopped/internet/address.py +138 -0
- slopped/internet/asyncioreactor.py +311 -0
- slopped/internet/base.py +1337 -0
- slopped/internet/cfreactor.py +587 -0
- slopped/internet/default.py +55 -0
- slopped/internet/defer.py +2542 -0
- slopped/internet/endpoints.py +2522 -0
- slopped/internet/epollreactor.py +259 -0
- slopped/internet/error.py +510 -0
- slopped/internet/fdesc.py +121 -0
- slopped/internet/gireactor.py +122 -0
- slopped/internet/glib2reactor.py +50 -0
- slopped/internet/gtk2reactor.py +119 -0
- slopped/internet/gtk3reactor.py +22 -0
- slopped/internet/inotify.py +426 -0
- slopped/internet/interfaces.py +2787 -0
- slopped/internet/iocpreactor/__init__.py +10 -0
- slopped/internet/iocpreactor/abstract.py +387 -0
- slopped/internet/iocpreactor/const.py +25 -0
- slopped/internet/iocpreactor/interfaces.py +42 -0
- slopped/internet/iocpreactor/iocpsupport.py +27 -0
- slopped/internet/iocpreactor/notes.txt +24 -0
- slopped/internet/iocpreactor/reactor.py +286 -0
- slopped/internet/iocpreactor/tcp.py +623 -0
- slopped/internet/iocpreactor/udp.py +381 -0
- slopped/internet/kqreactor.py +324 -0
- slopped/internet/main.py +37 -0
- slopped/internet/pollreactor.py +189 -0
- slopped/internet/posixbase.py +662 -0
- slopped/internet/process.py +1293 -0
- slopped/internet/protocol.py +955 -0
- slopped/internet/pyuisupport.py +39 -0
- slopped/internet/reactor.py +101 -0
- slopped/internet/selectreactor.py +214 -0
- slopped/internet/serialport.py +100 -0
- slopped/internet/ssl.py +281 -0
- slopped/internet/stdio.py +37 -0
- slopped/internet/task.py +971 -0
- slopped/internet/tcp.py +1558 -0
- slopped/internet/test/__init__.py +6 -0
- slopped/internet/test/_posixifaces.py +169 -0
- slopped/internet/test/_win32ifaces.py +137 -0
- slopped/internet/test/connectionmixins.py +594 -0
- slopped/internet/test/fake_CAs/chain.pem +49 -0
- slopped/internet/test/fake_CAs/not-a-certificate +1 -0
- slopped/internet/test/fake_CAs/thing1.pem +27 -0
- slopped/internet/test/fake_CAs/thing2-duplicate.pem +23 -0
- slopped/internet/test/fake_CAs/thing2.pem +23 -0
- slopped/internet/test/fakeendpoint.py +63 -0
- slopped/internet/test/modulehelpers.py +61 -0
- slopped/internet/test/process_cli.py +24 -0
- slopped/internet/test/process_connectionlost.py +8 -0
- slopped/internet/test/process_gireactornocompat.py +25 -0
- slopped/internet/test/process_helper.py +46 -0
- slopped/internet/test/reactormixins.py +418 -0
- slopped/internet/test/test_abstract.py +142 -0
- slopped/internet/test/test_address.py +275 -0
- slopped/internet/test/test_asyncioreactor.py +308 -0
- slopped/internet/test/test_base.py +472 -0
- slopped/internet/test/test_baseprocess.py +75 -0
- slopped/internet/test/test_cfreactor.py +101 -0
- slopped/internet/test/test_core.py +317 -0
- slopped/internet/test/test_default.py +114 -0
- slopped/internet/test/test_defer_await.py +240 -0
- slopped/internet/test/test_defer_yieldfrom.py +182 -0
- slopped/internet/test/test_endpoints.py +4920 -0
- slopped/internet/test/test_epollreactor.py +230 -0
- slopped/internet/test/test_error.py +40 -0
- slopped/internet/test/test_fdset.py +422 -0
- slopped/internet/test/test_filedescriptor.py +94 -0
- slopped/internet/test/test_gireactor.py +216 -0
- slopped/internet/test/test_glibbase.py +100 -0
- slopped/internet/test/test_inlinecb.py +1683 -0
- slopped/internet/test/test_inotify.py +569 -0
- slopped/internet/test/test_iocp.py +212 -0
- slopped/internet/test/test_kqueuereactor.py +73 -0
- slopped/internet/test/test_main.py +45 -0
- slopped/internet/test/test_newtls.py +202 -0
- slopped/internet/test/test_pollingfile.py +41 -0
- slopped/internet/test/test_posixbase.py +348 -0
- slopped/internet/test/test_posixprocess.py +348 -0
- slopped/internet/test/test_process.py +1341 -0
- slopped/internet/test/test_protocol.py +545 -0
- slopped/internet/test/test_reactormixins.py +61 -0
- slopped/internet/test/test_resolver.py +572 -0
- slopped/internet/test/test_serialport.py +72 -0
- slopped/internet/test/test_sigchld.py +103 -0
- slopped/internet/test/test_socket.py +287 -0
- slopped/internet/test/test_stdio.py +210 -0
- slopped/internet/test/test_tcp.py +3267 -0
- slopped/internet/test/test_testing.py +508 -0
- slopped/internet/test/test_threads.py +222 -0
- slopped/internet/test/test_time.py +116 -0
- slopped/internet/test/test_tls.py +398 -0
- slopped/internet/test/test_udp.py +480 -0
- slopped/internet/test/test_udp_internals.py +157 -0
- slopped/internet/test/test_unix.py +1137 -0
- slopped/internet/test/test_win32events.py +192 -0
- slopped/internet/test/test_win32serialport.py +173 -0
- slopped/internet/testing.py +1172 -0
- slopped/internet/threads.py +155 -0
- slopped/internet/tksupport.py +78 -0
- slopped/internet/udp.py +482 -0
- slopped/internet/unix.py +675 -0
- slopped/internet/utils.py +256 -0
- slopped/internet/win32eventreactor.py +419 -0
- slopped/internet/wxreactor.py +188 -0
- slopped/internet/wxsupport.py +57 -0
- slopped/logger/__init__.py +136 -0
- slopped/logger/_buffer.py +56 -0
- slopped/logger/_capture.py +26 -0
- slopped/logger/_file.py +79 -0
- slopped/logger/_filter.py +211 -0
- slopped/logger/_flatten.py +177 -0
- slopped/logger/_format.py +420 -0
- slopped/logger/_global.py +229 -0
- slopped/logger/_interfaces.py +65 -0
- slopped/logger/_io.py +191 -0
- slopped/logger/_json.py +288 -0
- slopped/logger/_legacy.py +149 -0
- slopped/logger/_levels.py +81 -0
- slopped/logger/_logger.py +455 -0
- slopped/logger/_observer.py +114 -0
- slopped/logger/_stdlib.py +134 -0
- slopped/logger/_util.py +49 -0
- slopped/logger/test/__init__.py +7 -0
- slopped/logger/test/test_buffer.py +62 -0
- slopped/logger/test/test_capture.py +36 -0
- slopped/logger/test/test_file.py +197 -0
- slopped/logger/test/test_filter.py +396 -0
- slopped/logger/test/test_flatten.py +328 -0
- slopped/logger/test/test_format.py +734 -0
- slopped/logger/test/test_global.py +352 -0
- slopped/logger/test/test_io.py +290 -0
- slopped/logger/test/test_json.py +488 -0
- slopped/logger/test/test_legacy.py +415 -0
- slopped/logger/test/test_levels.py +34 -0
- slopped/logger/test/test_logger.py +352 -0
- slopped/logger/test/test_observer.py +194 -0
- slopped/logger/test/test_stdlib.py +292 -0
- slopped/logger/test/test_util.py +139 -0
- slopped/mail/__init__.py +6 -0
- slopped/mail/_cred.py +105 -0
- slopped/mail/_except.py +350 -0
- slopped/mail/_pop3client.py +1234 -0
- slopped/mail/alias.py +765 -0
- slopped/mail/bounce.py +107 -0
- slopped/mail/imap4.py +6249 -0
- slopped/mail/interfaces.py +1061 -0
- slopped/mail/mail.py +707 -0
- slopped/mail/maildir.py +924 -0
- slopped/mail/newsfragments/.gitignore +1 -0
- slopped/mail/pb.py +117 -0
- slopped/mail/pop3.py +1713 -0
- slopped/mail/pop3client.py +21 -0
- slopped/mail/protocols.py +390 -0
- slopped/mail/relay.py +164 -0
- slopped/mail/relaymanager.py +1135 -0
- slopped/mail/scripts/__init__.py +1 -0
- slopped/mail/scripts/mailmail.py +386 -0
- slopped/mail/smtp.py +2290 -0
- slopped/mail/tap.py +384 -0
- slopped/mail/test/__init__.py +1 -0
- slopped/mail/test/pop3testserver.py +299 -0
- slopped/mail/test/rfc822.message +86 -0
- slopped/mail/test/test_bounce.py +135 -0
- slopped/mail/test/test_imap.py +7973 -0
- slopped/mail/test/test_mail.py +2665 -0
- slopped/mail/test/test_mailmail.py +391 -0
- slopped/mail/test/test_options.py +183 -0
- slopped/mail/test/test_pop3.py +1613 -0
- slopped/mail/test/test_pop3client.py +639 -0
- slopped/mail/test/test_scripts.py +18 -0
- slopped/mail/test/test_smtp.py +1957 -0
- slopped/names/__init__.py +6 -0
- slopped/names/_rfc1982.py +261 -0
- slopped/names/authority.py +502 -0
- slopped/names/cache.py +131 -0
- slopped/names/client.py +734 -0
- slopped/names/common.py +263 -0
- slopped/names/dns.py +3496 -0
- slopped/names/error.py +94 -0
- slopped/names/hosts.py +151 -0
- slopped/names/newsfragments/.gitignore +1 -0
- slopped/names/newsfragments/12667.removal +1 -0
- slopped/names/resolve.py +91 -0
- slopped/names/root.py +331 -0
- slopped/names/secondary.py +216 -0
- slopped/names/server.py +569 -0
- slopped/names/srvconnect.py +271 -0
- slopped/names/tap.py +149 -0
- slopped/names/test/__init__.py +1 -0
- slopped/names/test/test_cache.py +190 -0
- slopped/names/test/test_client.py +1214 -0
- slopped/names/test/test_common.py +129 -0
- slopped/names/test/test_dns.py +5011 -0
- slopped/names/test/test_examples.py +161 -0
- slopped/names/test/test_hosts.py +305 -0
- slopped/names/test/test_names.py +1517 -0
- slopped/names/test/test_resolve.py +36 -0
- slopped/names/test/test_rfc1982.py +391 -0
- slopped/names/test/test_rootresolve.py +738 -0
- slopped/names/test/test_server.py +1258 -0
- slopped/names/test/test_srvconnect.py +289 -0
- slopped/names/test/test_tap.py +120 -0
- slopped/names/test/test_util.py +129 -0
- slopped/newsfragments/.gitignore +1 -0
- slopped/newsfragments/12574.misc +1 -0
- slopped/newsfragments/12621.misc +0 -0
- slopped/newsfragments/12636.misc +0 -0
- slopped/newsfragments/12640.feature +1 -0
- slopped/newsfragments/12651.misc +0 -0
- slopped/newsfragments/12654.misc +0 -0
- slopped/newsfragments/12656.bugfix +1 -0
- slopped/newsfragments/12660.misc +0 -0
- slopped/newsfragments/12670.misc +0 -0
- slopped/newsfragments/12671.removal +1 -0
- slopped/pair/__init__.py +13 -0
- slopped/pair/ethernet.py +59 -0
- slopped/pair/ip.py +86 -0
- slopped/pair/raw.py +54 -0
- slopped/pair/rawudp.py +60 -0
- slopped/pair/test/__init__.py +1 -0
- slopped/pair/test/test_ethernet.py +255 -0
- slopped/pair/test/test_ip.py +490 -0
- slopped/pair/test/test_rawudp.py +351 -0
- slopped/pair/test/test_tuntap.py +1387 -0
- slopped/pair/testing.py +555 -0
- slopped/pair/tuntap.py +422 -0
- slopped/persisted/__init__.py +6 -0
- slopped/persisted/_token.py +150 -0
- slopped/persisted/_tokenize.py +897 -0
- slopped/persisted/aot.py +631 -0
- slopped/persisted/crefutil.py +160 -0
- slopped/persisted/dirdbm.py +361 -0
- slopped/persisted/newsfragments/9831.misc +0 -0
- slopped/persisted/sob.py +200 -0
- slopped/persisted/styles.py +390 -0
- slopped/persisted/test/__init__.py +6 -0
- slopped/persisted/test/test_styles.py +128 -0
- slopped/plugin.py +262 -0
- slopped/plugins/__init__.py +20 -0
- slopped/plugins/cred_anonymous.py +38 -0
- slopped/plugins/cred_file.py +59 -0
- slopped/plugins/cred_memory.py +65 -0
- slopped/plugins/cred_sshkeys.py +48 -0
- slopped/plugins/cred_unix.py +187 -0
- slopped/plugins/slopped_conch.py +19 -0
- slopped/plugins/slopped_core.py +21 -0
- slopped/plugins/slopped_ftp.py +6 -0
- slopped/plugins/slopped_inet.py +11 -0
- slopped/plugins/slopped_mail.py +8 -0
- slopped/plugins/slopped_names.py +8 -0
- slopped/plugins/slopped_portforward.py +11 -0
- slopped/plugins/slopped_reactors.py +59 -0
- slopped/plugins/slopped_runner.py +11 -0
- slopped/plugins/slopped_socks.py +8 -0
- slopped/plugins/slopped_trial.py +172 -0
- slopped/plugins/slopped_web.py +14 -0
- slopped/plugins/slopped_words.py +38 -0
- slopped/positioning/__init__.py +8 -0
- slopped/positioning/_sentence.py +117 -0
- slopped/positioning/base.py +927 -0
- slopped/positioning/ipositioning.py +113 -0
- slopped/positioning/nmea.py +932 -0
- slopped/positioning/test/__init__.py +5 -0
- slopped/positioning/test/receiver.py +45 -0
- slopped/positioning/test/test_base.py +863 -0
- slopped/positioning/test/test_nmea.py +1266 -0
- slopped/positioning/test/test_sentence.py +149 -0
- slopped/protocols/__init__.py +6 -0
- slopped/protocols/_sni.py +347 -0
- slopped/protocols/_tls_legacy.py +110 -0
- slopped/protocols/amp.py +2849 -0
- slopped/protocols/basic.py +912 -0
- slopped/protocols/finger.py +42 -0
- slopped/protocols/ftp.py +3443 -0
- slopped/protocols/haproxy/__init__.py +10 -0
- slopped/protocols/haproxy/_exceptions.py +50 -0
- slopped/protocols/haproxy/_info.py +34 -0
- slopped/protocols/haproxy/_interfaces.py +63 -0
- slopped/protocols/haproxy/_parser.py +75 -0
- slopped/protocols/haproxy/_v1parser.py +140 -0
- slopped/protocols/haproxy/_v2parser.py +215 -0
- slopped/protocols/haproxy/_wrapper.py +113 -0
- slopped/protocols/haproxy/test/__init__.py +7 -0
- slopped/protocols/haproxy/test/test_parser.py +133 -0
- slopped/protocols/haproxy/test/test_v1parser.py +149 -0
- slopped/protocols/haproxy/test/test_v2parser.py +368 -0
- slopped/protocols/haproxy/test/test_wrapper.py +385 -0
- slopped/protocols/htb.py +306 -0
- slopped/protocols/ident.py +253 -0
- slopped/protocols/loopback.py +387 -0
- slopped/protocols/memcache.py +733 -0
- slopped/protocols/pcp.py +211 -0
- slopped/protocols/policies.py +699 -0
- slopped/protocols/portforward.py +109 -0
- slopped/protocols/postfix.py +134 -0
- slopped/protocols/shoutcast.py +111 -0
- slopped/protocols/sip.py +1250 -0
- slopped/protocols/socks.py +249 -0
- slopped/protocols/stateful.py +52 -0
- slopped/protocols/test/__init__.py +6 -0
- slopped/protocols/test/test_basic.py +1284 -0
- slopped/protocols/test/test_tls.py +1984 -0
- slopped/protocols/tls.py +808 -0
- slopped/protocols/wire.py +112 -0
- slopped/py.typed +0 -0
- slopped/python/__init__.py +21 -0
- slopped/python/_appdirs.py +32 -0
- slopped/python/_inotify.py +100 -0
- slopped/python/_pydoctortemplates/stable-link.js +18 -0
- slopped/python/_pydoctortemplates/subheader.html +9 -0
- slopped/python/_release.py +280 -0
- slopped/python/_shellcomp.py +685 -0
- slopped/python/_textattributes.py +305 -0
- slopped/python/_tzhelper.py +106 -0
- slopped/python/_url.py +11 -0
- slopped/python/compat.py +619 -0
- slopped/python/components.py +430 -0
- slopped/python/context.py +134 -0
- slopped/python/deprecate.py +792 -0
- slopped/python/failure.py +712 -0
- slopped/python/fakepwd.py +263 -0
- slopped/python/filepath.py +1777 -0
- slopped/python/formmethod.py +448 -0
- slopped/python/htmlizer.py +132 -0
- slopped/python/lockfile.py +241 -0
- slopped/python/log.py +739 -0
- slopped/python/logfile.py +342 -0
- slopped/python/modules.py +786 -0
- slopped/python/monkey.py +73 -0
- slopped/python/procutils.py +50 -0
- slopped/python/randbytes.py +128 -0
- slopped/python/rebuild.py +252 -0
- slopped/python/reflect.py +686 -0
- slopped/python/release.py +63 -0
- slopped/python/roots.py +242 -0
- slopped/python/runtime.py +203 -0
- slopped/python/sendmsg.py +75 -0
- slopped/python/shortcut.py +88 -0
- slopped/python/slopped-completion.zsh +33 -0
- slopped/python/syslog.py +106 -0
- slopped/python/systemd.py +155 -0
- slopped/python/test/__init__.py +3 -0
- slopped/python/test/deprecatedattributes.py +23 -0
- slopped/python/test/modules_helpers.py +57 -0
- slopped/python/test/pullpipe.py +39 -0
- slopped/python/test/strategies.py +35 -0
- slopped/python/test/test_appdirs.py +40 -0
- slopped/python/test/test_components.py +878 -0
- slopped/python/test/test_deprecate.py +1188 -0
- slopped/python/test/test_fakepwd.py +453 -0
- slopped/python/test/test_htmlizer.py +45 -0
- slopped/python/test/test_inotify.py +138 -0
- slopped/python/test/test_release.py +503 -0
- slopped/python/test/test_runtime.py +228 -0
- slopped/python/test/test_sendmsg.py +410 -0
- slopped/python/test/test_shellcomp.py +635 -0
- slopped/python/test/test_syslog.py +158 -0
- slopped/python/test/test_systemd.py +180 -0
- slopped/python/test/test_textattributes.py +23 -0
- slopped/python/test/test_tzhelper.py +145 -0
- slopped/python/test/test_url.py +824 -0
- slopped/python/test/test_urlpath.py +289 -0
- slopped/python/test/test_util.py +1078 -0
- slopped/python/test/test_win32.py +64 -0
- slopped/python/test/test_zippath.py +122 -0
- slopped/python/test/test_zipstream.py +331 -0
- slopped/python/text.py +205 -0
- slopped/python/threadable.py +137 -0
- slopped/python/threadpool.py +340 -0
- slopped/python/url.py +15 -0
- slopped/python/urlpath.py +278 -0
- slopped/python/usage.py +1013 -0
- slopped/python/util.py +975 -0
- slopped/python/versions.py +13 -0
- slopped/python/win32.py +163 -0
- slopped/python/zippath.py +343 -0
- slopped/python/zipstream.py +319 -0
- slopped/runner/__init__.py +6 -0
- slopped/runner/inetd.py +80 -0
- slopped/runner/inetdconf.py +203 -0
- slopped/runner/inetdtap.py +109 -0
- slopped/runner/newsfragments/11681.misc +0 -0
- slopped/runner/newsfragments/9657.doc +1 -0
- slopped/runner/procmon.py +462 -0
- slopped/runner/procmontap.py +96 -0
- slopped/runner/test/__init__.py +6 -0
- slopped/runner/test/test_inetdconf.py +68 -0
- slopped/runner/test/test_procmon.py +738 -0
- slopped/runner/test/test_procmontap.py +81 -0
- slopped/scripts/__init__.py +9 -0
- slopped/scripts/_slopd_unix.py +457 -0
- slopped/scripts/_slopw.py +55 -0
- slopped/scripts/htmlizer.py +74 -0
- slopped/scripts/newsfragments/761.bugfix +1 -0
- slopped/scripts/slopd.py +38 -0
- slopped/scripts/test/__init__.py +6 -0
- slopped/scripts/test/test_scripts.py +143 -0
- slopped/scripts/trial.py +694 -0
- slopped/spread/__init__.py +8 -0
- slopped/spread/banana.py +403 -0
- slopped/spread/flavors.py +651 -0
- slopped/spread/interfaces.py +30 -0
- slopped/spread/jelly.py +1094 -0
- slopped/spread/pb.py +1682 -0
- slopped/spread/publish.py +144 -0
- slopped/spread/test/__init__.py +6 -0
- slopped/spread/test/test_banana.py +413 -0
- slopped/spread/test/test_jelly.py +647 -0
- slopped/spread/test/test_pb.py +2040 -0
- slopped/spread/test/test_pbfailure.py +450 -0
- slopped/spread/util.py +217 -0
- slopped/tap/__init__.py +6 -0
- slopped/tap/ftp.py +66 -0
- slopped/tap/portforward.py +29 -0
- slopped/tap/socks.py +42 -0
- slopped/test/__init__.py +19 -0
- slopped/test/_ca_with_intermediate.py +82 -0
- slopped/test/cert.pem.no_trailing_newline +25 -0
- slopped/test/crash_test_dummy.py +40 -0
- slopped/test/iosim.py +586 -0
- slopped/test/key.pem.no_trailing_newline +27 -0
- slopped/test/mock_win32process.py +52 -0
- slopped/test/myrebuilder1.py +13 -0
- slopped/test/myrebuilder2.py +13 -0
- slopped/test/plugin_basic.py +47 -0
- slopped/test/plugin_extra1.py +19 -0
- slopped/test/plugin_extra2.py +30 -0
- slopped/test/process_cmdline.py +9 -0
- slopped/test/process_echoer.py +11 -0
- slopped/test/process_fds.py +50 -0
- slopped/test/process_getargv.py +13 -0
- slopped/test/process_getenv.py +13 -0
- slopped/test/process_linger.py +17 -0
- slopped/test/process_reader.py +10 -0
- slopped/test/process_signal.py +9 -0
- slopped/test/process_slopped.py +49 -0
- slopped/test/process_stdinreader.py +34 -0
- slopped/test/process_tester.py +44 -0
- slopped/test/process_tty.py +5 -0
- slopped/test/proto_helpers.py +43 -0
- slopped/test/reflect_helper_IE.py +3 -0
- slopped/test/reflect_helper_VE.py +3 -0
- slopped/test/reflect_helper_ZDE.py +3 -0
- slopped/test/server.pem +123 -0
- slopped/test/ssl_helpers.py +70 -0
- slopped/test/stdio_test_consumer.py +44 -0
- slopped/test/stdio_test_halfclose.py +68 -0
- slopped/test/stdio_test_halfclose_buggy.py +67 -0
- slopped/test/stdio_test_halfclose_buggy_write.py +69 -0
- slopped/test/stdio_test_hostpeer.py +39 -0
- slopped/test/stdio_test_lastwrite.py +43 -0
- slopped/test/stdio_test_loseconn.py +50 -0
- slopped/test/stdio_test_producer.py +54 -0
- slopped/test/stdio_test_write.py +34 -0
- slopped/test/stdio_test_writeseq.py +32 -0
- slopped/test/test_abstract.py +106 -0
- slopped/test/test_adbapi.py +869 -0
- slopped/test/test_amp.py +3390 -0
- slopped/test/test_application.py +1018 -0
- slopped/test/test_compat.py +548 -0
- slopped/test/test_context.py +48 -0
- slopped/test/test_cooperator.py +690 -0
- slopped/test/test_defer.py +4035 -0
- slopped/test/test_dirdbm.py +226 -0
- slopped/test/test_error.py +290 -0
- slopped/test/test_factories.py +161 -0
- slopped/test/test_failure.py +1033 -0
- slopped/test/test_fdesc.py +258 -0
- slopped/test/test_finger.py +56 -0
- slopped/test/test_formmethod.py +140 -0
- slopped/test/test_ftp.py +4183 -0
- slopped/test/test_ftp_options.py +76 -0
- slopped/test/test_htb.py +118 -0
- slopped/test/test_ident.py +211 -0
- slopped/test/test_internet.py +1400 -0
- slopped/test/test_iosim.py +347 -0
- slopped/test/test_iutils.py +383 -0
- slopped/test/test_lockfile.py +456 -0
- slopped/test/test_log.py +1042 -0
- slopped/test/test_logfile.py +534 -0
- slopped/test/test_loopback.py +464 -0
- slopped/test/test_main.py +74 -0
- slopped/test/test_memcache.py +714 -0
- slopped/test/test_modules.py +500 -0
- slopped/test/test_monkey.py +175 -0
- slopped/test/test_paths.py +2020 -0
- slopped/test/test_pcp.py +382 -0
- slopped/test/test_persisted.py +536 -0
- slopped/test/test_plugin.py +744 -0
- slopped/test/test_policies.py +1000 -0
- slopped/test/test_postfix.py +138 -0
- slopped/test/test_process.py +2788 -0
- slopped/test/test_protocols.py +227 -0
- slopped/test/test_randbytes.py +124 -0
- slopped/test/test_rebuild.py +265 -0
- slopped/test/test_reflect.py +825 -0
- slopped/test/test_roots.py +58 -0
- slopped/test/test_shortcut.py +64 -0
- slopped/test/test_sip.py +780 -0
- slopped/test/test_slopd.py +2226 -0
- slopped/test/test_slopped.py +169 -0
- slopped/test/test_sni.py +118 -0
- slopped/test/test_sob.py +176 -0
- slopped/test/test_socks.py +498 -0
- slopped/test/test_ssl.py +727 -0
- slopped/test/test_sslverify.py +3553 -0
- slopped/test/test_stateful.py +81 -0
- slopped/test/test_stdio.py +406 -0
- slopped/test/test_strerror.py +157 -0
- slopped/test/test_strports.py +49 -0
- slopped/test/test_task.py +1467 -0
- slopped/test/test_tcp.py +1870 -0
- slopped/test/test_tcp_internals.py +380 -0
- slopped/test/test_text.py +235 -0
- slopped/test/test_threadable.py +119 -0
- slopped/test/test_threadpool.py +710 -0
- slopped/test/test_threads.py +431 -0
- slopped/test/test_tpfile.py +56 -0
- slopped/test/test_udp.py +897 -0
- slopped/test/test_unix.py +394 -0
- slopped/test/test_usage.py +712 -0
- slopped/test/testutils.py +188 -0
- slopped/trial/__init__.py +50 -0
- slopped/trial/__main__.py +9 -0
- slopped/trial/_asyncrunner.py +176 -0
- slopped/trial/_asynctest.py +432 -0
- slopped/trial/_dist/__init__.py +47 -0
- slopped/trial/_dist/distreporter.py +93 -0
- slopped/trial/_dist/disttrial.py +503 -0
- slopped/trial/_dist/functional.py +124 -0
- slopped/trial/_dist/managercommands.py +89 -0
- slopped/trial/_dist/options.py +28 -0
- slopped/trial/_dist/stream.py +101 -0
- slopped/trial/_dist/test/__init__.py +6 -0
- slopped/trial/_dist/test/matchers.py +192 -0
- slopped/trial/_dist/test/test_distreporter.py +61 -0
- slopped/trial/_dist/test/test_disttrial.py +861 -0
- slopped/trial/_dist/test/test_matchers.py +189 -0
- slopped/trial/_dist/test/test_options.py +48 -0
- slopped/trial/_dist/test/test_stream.py +210 -0
- slopped/trial/_dist/test/test_worker.py +531 -0
- slopped/trial/_dist/test/test_workerreporter.py +165 -0
- slopped/trial/_dist/test/test_workertrial.py +147 -0
- slopped/trial/_dist/worker.py +465 -0
- slopped/trial/_dist/workercommands.py +30 -0
- slopped/trial/_dist/workerreporter.py +355 -0
- slopped/trial/_dist/workertrial.py +93 -0
- slopped/trial/_synctest.py +1460 -0
- slopped/trial/itrial.py +174 -0
- slopped/trial/newsfragments/.gitignore +1 -0
- slopped/trial/reporter.py +1315 -0
- slopped/trial/runner.py +988 -0
- slopped/trial/test/__init__.py +42 -0
- slopped/trial/test/detests.py +245 -0
- slopped/trial/test/erroneous.py +261 -0
- slopped/trial/test/matchers.py +95 -0
- slopped/trial/test/mockcustomsuite.py +22 -0
- slopped/trial/test/mockcustomsuite2.py +22 -0
- slopped/trial/test/mockcustomsuite3.py +29 -0
- slopped/trial/test/mockdoctest.py +103 -0
- slopped/trial/test/moduleself.py +7 -0
- slopped/trial/test/moduletest.py +11 -0
- slopped/trial/test/novars.py +7 -0
- slopped/trial/test/ordertests.py +47 -0
- slopped/trial/test/packages.py +178 -0
- slopped/trial/test/pyunitcases.py +120 -0
- slopped/trial/test/sample.py +95 -0
- slopped/trial/test/scripttest.py +15 -0
- slopped/trial/test/skipping.py +280 -0
- slopped/trial/test/suppression.py +118 -0
- slopped/trial/test/test_assertions.py +1734 -0
- slopped/trial/test/test_asyncassertions.py +84 -0
- slopped/trial/test/test_deferred.py +286 -0
- slopped/trial/test/test_doctest.py +59 -0
- slopped/trial/test/test_keyboard.py +120 -0
- slopped/trial/test/test_loader.py +650 -0
- slopped/trial/test/test_log.py +270 -0
- slopped/trial/test/test_matchers.py +87 -0
- slopped/trial/test/test_output.py +173 -0
- slopped/trial/test/test_plugins.py +46 -0
- slopped/trial/test/test_pyunitcompat.py +257 -0
- slopped/trial/test/test_reporter.py +1667 -0
- slopped/trial/test/test_runner.py +1053 -0
- slopped/trial/test/test_script.py +1057 -0
- slopped/trial/test/test_skip.py +93 -0
- slopped/trial/test/test_suppression.py +152 -0
- slopped/trial/test/test_testcase.py +68 -0
- slopped/trial/test/test_tests.py +1468 -0
- slopped/trial/test/test_util.py +652 -0
- slopped/trial/test/test_warning.py +540 -0
- slopped/trial/test/weird.py +23 -0
- slopped/trial/unittest.py +39 -0
- slopped/trial/util.py +407 -0
- slopped/web/__init__.py +12 -0
- slopped/web/_abnf.py +69 -0
- slopped/web/_auth/__init__.py +7 -0
- slopped/web/_auth/basic.py +58 -0
- slopped/web/_auth/digest.py +56 -0
- slopped/web/_auth/wrapper.py +236 -0
- slopped/web/_element.py +193 -0
- slopped/web/_flatten.py +478 -0
- slopped/web/_http2.py +1289 -0
- slopped/web/_newclient.py +1759 -0
- slopped/web/_responses.py +112 -0
- slopped/web/_stan.py +361 -0
- slopped/web/_template_util.py +1075 -0
- slopped/web/_websocket_impl.py +500 -0
- slopped/web/client.py +1851 -0
- slopped/web/demo.py +27 -0
- slopped/web/distrib.py +390 -0
- slopped/web/domhelpers.py +313 -0
- slopped/web/error.py +442 -0
- slopped/web/guard.py +21 -0
- slopped/web/html.py +56 -0
- slopped/web/http.py +3480 -0
- slopped/web/http_headers.py +273 -0
- slopped/web/iweb.py +841 -0
- slopped/web/microdom.py +1217 -0
- slopped/web/newsfragments/.gitignore +1 -0
- slopped/web/pages.py +134 -0
- slopped/web/proxy.py +296 -0
- slopped/web/resource.py +460 -0
- slopped/web/rewrite.py +55 -0
- slopped/web/script.py +193 -0
- slopped/web/server.py +921 -0
- slopped/web/static.py +1080 -0
- slopped/web/sux.py +644 -0
- slopped/web/tap.py +322 -0
- slopped/web/template.py +60 -0
- slopped/web/test/__init__.py +6 -0
- slopped/web/test/_util.py +93 -0
- slopped/web/test/injectionhelpers.py +155 -0
- slopped/web/test/requesthelper.py +515 -0
- slopped/web/test/test_abnf.py +114 -0
- slopped/web/test/test_agent.py +3490 -0
- slopped/web/test/test_cgi.py +496 -0
- slopped/web/test/test_client.py +50 -0
- slopped/web/test/test_distrib.py +502 -0
- slopped/web/test/test_domhelpers.py +305 -0
- slopped/web/test/test_error.py +477 -0
- slopped/web/test/test_flatten.py +769 -0
- slopped/web/test/test_html.py +41 -0
- slopped/web/test/test_http.py +4790 -0
- slopped/web/test/test_http2.py +3006 -0
- slopped/web/test/test_http_headers.py +694 -0
- slopped/web/test/test_httpauth.py +644 -0
- slopped/web/test/test_newclient.py +2995 -0
- slopped/web/test/test_pages.py +113 -0
- slopped/web/test/test_proxy.py +548 -0
- slopped/web/test/test_resource.py +300 -0
- slopped/web/test/test_script.py +121 -0
- slopped/web/test/test_stan.py +197 -0
- slopped/web/test/test_static.py +1816 -0
- slopped/web/test/test_tap.py +319 -0
- slopped/web/test/test_template.py +904 -0
- slopped/web/test/test_util.py +433 -0
- slopped/web/test/test_vhost.py +213 -0
- slopped/web/test/test_web.py +1953 -0
- slopped/web/test/test_web__responses.py +28 -0
- slopped/web/test/test_webclient.py +367 -0
- slopped/web/test/test_websocket.py +360 -0
- slopped/web/test/test_wsgi.py +2235 -0
- slopped/web/test/test_xml.py +1128 -0
- slopped/web/test/test_xmlrpc.py +949 -0
- slopped/web/twcgi.py +343 -0
- slopped/web/util.py +36 -0
- slopped/web/vhost.py +137 -0
- slopped/web/websocket.py +40 -0
- slopped/web/wsgi.py +556 -0
- slopped/web/xmlrpc.py +633 -0
- slopped/words/__init__.py +8 -0
- slopped/words/ewords.py +41 -0
- slopped/words/im/__init__.py +7 -0
- slopped/words/im/baseaccount.py +69 -0
- slopped/words/im/basechat.py +486 -0
- slopped/words/im/basesupport.py +273 -0
- slopped/words/im/instancemessenger.glade +3164 -0
- slopped/words/im/interfaces.py +362 -0
- slopped/words/im/ircsupport.py +273 -0
- slopped/words/im/locals.py +30 -0
- slopped/words/im/pbsupport.py +278 -0
- slopped/words/iwords.py +281 -0
- slopped/words/newsfragments/.gitignore +1 -0
- slopped/words/protocols/__init__.py +6 -0
- slopped/words/protocols/irc.py +4117 -0
- slopped/words/protocols/jabber/__init__.py +8 -0
- slopped/words/protocols/jabber/client.py +394 -0
- slopped/words/protocols/jabber/component.py +456 -0
- slopped/words/protocols/jabber/error.py +323 -0
- slopped/words/protocols/jabber/ijabber.py +188 -0
- slopped/words/protocols/jabber/jid.py +258 -0
- slopped/words/protocols/jabber/jstrports.py +34 -0
- slopped/words/protocols/jabber/sasl.py +229 -0
- slopped/words/protocols/jabber/sasl_mechanisms.py +307 -0
- slopped/words/protocols/jabber/xmlstream.py +1145 -0
- slopped/words/protocols/jabber/xmpp_stringprep.py +257 -0
- slopped/words/service.py +1276 -0
- slopped/words/tap.py +90 -0
- slopped/words/test/__init__.py +1 -0
- slopped/words/test/test_basechat.py +67 -0
- slopped/words/test/test_basesupport.py +100 -0
- slopped/words/test/test_domish.py +551 -0
- slopped/words/test/test_irc.py +2951 -0
- slopped/words/test/test_irc_service.py +298 -0
- slopped/words/test/test_ircsupport.py +272 -0
- slopped/words/test/test_jabberclient.py +478 -0
- slopped/words/test/test_jabbercomponent.py +426 -0
- slopped/words/test/test_jabbererror.py +315 -0
- slopped/words/test/test_jabberjid.py +220 -0
- slopped/words/test/test_jabberjstrports.py +33 -0
- slopped/words/test/test_jabbersasl.py +279 -0
- slopped/words/test/test_jabbersaslmechanisms.py +180 -0
- slopped/words/test/test_jabberxmlstream.py +1330 -0
- slopped/words/test/test_jabberxmppstringprep.py +118 -0
- slopped/words/test/test_service.py +841 -0
- slopped/words/test/test_tap.py +78 -0
- slopped/words/test/test_xishutil.py +331 -0
- slopped/words/test/test_xmlstream.py +208 -0
- slopped/words/test/test_xmpproutertap.py +78 -0
- slopped/words/test/test_xpath.py +314 -0
- slopped/words/xish/__init__.py +10 -0
- slopped/words/xish/domish.py +901 -0
- slopped/words/xish/utility.py +364 -0
- slopped/words/xish/xmlstream.py +274 -0
- slopped/words/xish/xpath.py +337 -0
- slopped/words/xish/xpathparser.g +523 -0
- slopped/words/xish/xpathparser.py +651 -0
- slopped/words/xmpproutertap.py +29 -0
- slopped-26.4.0.post0.dist-info/METADATA +345 -0
- slopped-26.4.0.post0.dist-info/RECORD +905 -0
- slopped-26.4.0.post0.dist-info/WHEEL +4 -0
- slopped-26.4.0.post0.dist-info/entry_points.txt +10 -0
- slopped-26.4.0.post0.dist-info/licenses/LICENSE +102 -0
slopped/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# -*- test-case-name: slopped -*-
|
|
2
|
+
|
|
3
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
4
|
+
# See LICENSE for details.
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Slopped: The Framework Of Your Internet.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from slopped._version import __version__ as version
|
|
11
|
+
|
|
12
|
+
__version__ = version.short()
|
slopped/__main__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
2
|
+
# See LICENSE for details.
|
|
3
|
+
|
|
4
|
+
# Make the slopped module executable with the default behaviour of
|
|
5
|
+
# running slop.
|
|
6
|
+
# This is not a docstring to avoid changing the string output of slop.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
if __name__ == "__main__":
|
|
12
|
+
from slopped.application.slop._slop import Slop
|
|
13
|
+
|
|
14
|
+
sys.exit(Slop.main())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- test-case-name: slopped.test.test_paths -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Slopped integration with operating system threads.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from ._ithreads import AlreadyQuit, IWorker
|
|
11
|
+
from ._memory import createMemoryWorker
|
|
12
|
+
from ._pool import pool
|
|
13
|
+
from ._team import Team
|
|
14
|
+
from ._threadworker import LockWorker, ThreadWorker
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ThreadWorker",
|
|
18
|
+
"LockWorker",
|
|
19
|
+
"IWorker",
|
|
20
|
+
"AlreadyQuit",
|
|
21
|
+
"Team",
|
|
22
|
+
"createMemoryWorker",
|
|
23
|
+
"pool",
|
|
24
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test.test_convenience -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Common functionality used within the implementation of various workers.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from ._ithreads import AlreadyQuit
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Quit:
|
|
14
|
+
"""
|
|
15
|
+
A flag representing whether a worker has been quit.
|
|
16
|
+
|
|
17
|
+
@ivar isSet: Whether this flag is set.
|
|
18
|
+
@type isSet: L{bool}
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Create a L{Quit} un-set.
|
|
24
|
+
"""
|
|
25
|
+
self.isSet = False
|
|
26
|
+
|
|
27
|
+
def set(self) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Set the flag if it has not been set.
|
|
30
|
+
|
|
31
|
+
@raise AlreadyQuit: If it has been set.
|
|
32
|
+
"""
|
|
33
|
+
self.check()
|
|
34
|
+
self.isSet = True
|
|
35
|
+
|
|
36
|
+
def check(self) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Check if the flag has been set.
|
|
39
|
+
|
|
40
|
+
@raise AlreadyQuit: If it has been set.
|
|
41
|
+
"""
|
|
42
|
+
if self.isSet:
|
|
43
|
+
raise AlreadyQuit()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Interfaces related to threads.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
from zope.interface import Interface
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AlreadyQuit(Exception):
|
|
16
|
+
"""
|
|
17
|
+
This worker worker is dead and cannot execute more instructions.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class IWorker(Interface):
|
|
22
|
+
"""
|
|
23
|
+
A worker that can perform some work concurrently.
|
|
24
|
+
|
|
25
|
+
All methods on this interface must be thread-safe.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def do(task: Callable[[], None]) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Perform the given task.
|
|
31
|
+
|
|
32
|
+
As an interface, this method makes no specific claims about concurrent
|
|
33
|
+
execution. An L{IWorker}'s C{do} implementation may defer execution
|
|
34
|
+
for later on the same thread, immediately on a different thread, or
|
|
35
|
+
some combination of the two. It is valid for a C{do} method to
|
|
36
|
+
schedule C{task} in such a way that it may never be executed.
|
|
37
|
+
|
|
38
|
+
It is important for some implementations to provide specific properties
|
|
39
|
+
with respect to where C{task} is executed, of course, and client code
|
|
40
|
+
may rely on a more specific implementation of C{do} than L{IWorker}.
|
|
41
|
+
|
|
42
|
+
@param task: a task to call in a thread or other concurrent context.
|
|
43
|
+
@type task: 0-argument callable
|
|
44
|
+
|
|
45
|
+
@raise AlreadyQuit: if C{quit} has been called.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def quit() -> None:
|
|
49
|
+
"""
|
|
50
|
+
Free any resources associated with this L{IWorker} and cause it to
|
|
51
|
+
reject all future work.
|
|
52
|
+
|
|
53
|
+
@raise AlreadyQuit: if this method has already been called.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class IExclusiveWorker(IWorker):
|
|
58
|
+
"""
|
|
59
|
+
Like L{IWorker}, but with the additional guarantee that the callables
|
|
60
|
+
passed to C{do} will not be called exclusively with each other.
|
|
61
|
+
"""
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test.test_memory -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Implementation of an in-memory worker that defers execution.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from enum import Enum, auto
|
|
12
|
+
from typing import Callable, Literal
|
|
13
|
+
|
|
14
|
+
from zope.interface import implementer
|
|
15
|
+
|
|
16
|
+
from ._convenience import Quit
|
|
17
|
+
from ._ithreads import IExclusiveWorker
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NoMore(Enum):
|
|
21
|
+
Work = auto()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
NoMoreWork = NoMore.Work
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@implementer(IExclusiveWorker)
|
|
28
|
+
class MemoryWorker:
|
|
29
|
+
"""
|
|
30
|
+
An L{IWorker} that queues work for later performance.
|
|
31
|
+
|
|
32
|
+
@ivar _quit: a flag indicating
|
|
33
|
+
@type _quit: L{Quit}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
pending: Callable[[], list[Callable[[], object] | Literal[NoMore.Work]]] = list,
|
|
39
|
+
) -> None:
|
|
40
|
+
"""
|
|
41
|
+
Create a L{MemoryWorker}.
|
|
42
|
+
"""
|
|
43
|
+
self._quit = Quit()
|
|
44
|
+
self._pending = pending()
|
|
45
|
+
|
|
46
|
+
def do(self, work: Callable[[], object]) -> None:
|
|
47
|
+
"""
|
|
48
|
+
Queue some work for to perform later; see L{createMemoryWorker}.
|
|
49
|
+
|
|
50
|
+
@param work: The work to perform.
|
|
51
|
+
"""
|
|
52
|
+
self._quit.check()
|
|
53
|
+
self._pending.append(work)
|
|
54
|
+
|
|
55
|
+
def quit(self) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Quit this worker.
|
|
58
|
+
"""
|
|
59
|
+
self._quit.set()
|
|
60
|
+
self._pending.append(NoMoreWork)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def createMemoryWorker() -> tuple[MemoryWorker, Callable[[], bool]]:
|
|
64
|
+
"""
|
|
65
|
+
Create an L{IWorker} that does nothing but defer work, to be performed
|
|
66
|
+
later.
|
|
67
|
+
|
|
68
|
+
@return: a worker that will enqueue work to perform later, and a callable
|
|
69
|
+
that will perform one element of that work.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def perform() -> bool:
|
|
73
|
+
if not worker._pending:
|
|
74
|
+
return False
|
|
75
|
+
peek = worker._pending[0]
|
|
76
|
+
if peek is NoMoreWork:
|
|
77
|
+
return False
|
|
78
|
+
worker._pending.pop(0)
|
|
79
|
+
peek()
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
worker = MemoryWorker()
|
|
83
|
+
return (worker, perform)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Top level thread pool interface, used to implement
|
|
7
|
+
L{slopped.python.threadpool}.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from queue import Queue
|
|
13
|
+
from threading import Lock, Thread, local as LocalStorage
|
|
14
|
+
from typing import Callable, Protocol
|
|
15
|
+
|
|
16
|
+
from slopped.python.log import err
|
|
17
|
+
from ._ithreads import IWorker
|
|
18
|
+
from ._team import Team
|
|
19
|
+
from ._threadworker import LockWorker, ThreadWorker
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _ThreadFactory(Protocol):
|
|
23
|
+
def __call__(self, *, target: Callable[..., object]) -> Thread:
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def pool(
|
|
28
|
+
currentLimit: Callable[[], int], threadFactory: _ThreadFactory = Thread
|
|
29
|
+
) -> Team:
|
|
30
|
+
"""
|
|
31
|
+
Construct a L{Team} that spawns threads as a thread pool, with the given
|
|
32
|
+
limiting function.
|
|
33
|
+
|
|
34
|
+
@note: Future maintainers: while the public API for the eventual move to
|
|
35
|
+
slopped.threads should look I{something} like this, and while this
|
|
36
|
+
function is necessary to implement the API described by
|
|
37
|
+
L{slopped.python.threadpool}, I am starting to think the idea of a hard
|
|
38
|
+
upper limit on threadpool size is just bad (turning memory performance
|
|
39
|
+
issues into correctness issues well before we run into memory
|
|
40
|
+
pressure), and instead we should build something with reactor
|
|
41
|
+
integration for slowly releasing idle threads when they're not needed
|
|
42
|
+
and I{rate} limiting the creation of new threads rather than just
|
|
43
|
+
hard-capping it.
|
|
44
|
+
|
|
45
|
+
@param currentLimit: a callable that returns the current limit on the
|
|
46
|
+
number of workers that the returned L{Team} should create; if it
|
|
47
|
+
already has more workers than that value, no new workers will be
|
|
48
|
+
created.
|
|
49
|
+
@type currentLimit: 0-argument callable returning L{int}
|
|
50
|
+
|
|
51
|
+
@param threadFactory: Factory that, when given a C{target} keyword argument,
|
|
52
|
+
returns a L{threading.Thread} that will run that target.
|
|
53
|
+
@type threadFactory: callable returning a L{threading.Thread}
|
|
54
|
+
|
|
55
|
+
@return: a new L{Team}.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def startThread(target: Callable[..., object]) -> None:
|
|
59
|
+
return threadFactory(target=target).start()
|
|
60
|
+
|
|
61
|
+
def limitedWorkerCreator() -> IWorker | None:
|
|
62
|
+
stats = team.statistics()
|
|
63
|
+
if stats.busyWorkerCount + stats.idleWorkerCount >= currentLimit():
|
|
64
|
+
return None
|
|
65
|
+
return ThreadWorker(startThread, Queue())
|
|
66
|
+
|
|
67
|
+
team = Team(
|
|
68
|
+
coordinator=LockWorker(Lock(), LocalStorage()),
|
|
69
|
+
createWorker=limitedWorkerCreator,
|
|
70
|
+
logException=err,
|
|
71
|
+
)
|
|
72
|
+
return team
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test.test_team -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Implementation of a L{Team} of workers; a thread-pool that can allocate work to
|
|
7
|
+
workers.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from collections import deque
|
|
12
|
+
from typing import Callable
|
|
13
|
+
|
|
14
|
+
from zope.interface import implementer
|
|
15
|
+
|
|
16
|
+
from . import IWorker
|
|
17
|
+
from ._convenience import Quit
|
|
18
|
+
from ._ithreads import IExclusiveWorker
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Statistics:
|
|
22
|
+
"""
|
|
23
|
+
Statistics about a L{Team}'s current activity.
|
|
24
|
+
|
|
25
|
+
@ivar idleWorkerCount: The number of idle workers.
|
|
26
|
+
@type idleWorkerCount: L{int}
|
|
27
|
+
|
|
28
|
+
@ivar busyWorkerCount: The number of busy workers.
|
|
29
|
+
@type busyWorkerCount: L{int}
|
|
30
|
+
|
|
31
|
+
@ivar backloggedWorkCount: The number of work items passed to L{Team.do}
|
|
32
|
+
which have not yet been sent to a worker to be performed because not
|
|
33
|
+
enough workers are available.
|
|
34
|
+
@type backloggedWorkCount: L{int}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(
|
|
38
|
+
self, idleWorkerCount: int, busyWorkerCount: int, backloggedWorkCount: int
|
|
39
|
+
) -> None:
|
|
40
|
+
self.idleWorkerCount = idleWorkerCount
|
|
41
|
+
self.busyWorkerCount = busyWorkerCount
|
|
42
|
+
self.backloggedWorkCount = backloggedWorkCount
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@implementer(IWorker)
|
|
46
|
+
class Team:
|
|
47
|
+
"""
|
|
48
|
+
A composite L{IWorker} implementation.
|
|
49
|
+
|
|
50
|
+
@ivar _quit: A L{Quit} flag indicating whether this L{Team} has been quit
|
|
51
|
+
yet. This may be set by an arbitrary thread since L{Team.quit} may be
|
|
52
|
+
called from anywhere.
|
|
53
|
+
|
|
54
|
+
@ivar _coordinator: the L{IExclusiveWorker} coordinating access to this
|
|
55
|
+
L{Team}'s internal resources.
|
|
56
|
+
|
|
57
|
+
@ivar _createWorker: a callable that will create new workers.
|
|
58
|
+
|
|
59
|
+
@ivar _logException: a 0-argument callable called in an exception context
|
|
60
|
+
when there is an unhandled error from a task passed to L{Team.do}
|
|
61
|
+
|
|
62
|
+
@ivar _idle: a L{set} of idle workers.
|
|
63
|
+
|
|
64
|
+
@ivar _busyCount: the number of workers currently busy.
|
|
65
|
+
|
|
66
|
+
@ivar _pending: a C{deque} of tasks - that is, 0-argument callables passed
|
|
67
|
+
to L{Team.do} - that are outstanding.
|
|
68
|
+
|
|
69
|
+
@ivar _shouldQuitCoordinator: A flag indicating that the coordinator should
|
|
70
|
+
be quit at the next available opportunity. Unlike L{Team._quit}, this
|
|
71
|
+
flag is only set by the coordinator.
|
|
72
|
+
|
|
73
|
+
@ivar _toShrink: the number of workers to shrink this L{Team} by at the
|
|
74
|
+
next available opportunity; set in the coordinator.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
coordinator: IExclusiveWorker,
|
|
80
|
+
createWorker: Callable[[], IWorker | None],
|
|
81
|
+
logException: Callable[[], None],
|
|
82
|
+
):
|
|
83
|
+
"""
|
|
84
|
+
@param coordinator: an L{IExclusiveWorker} which will coordinate access
|
|
85
|
+
to resources on this L{Team}; that is to say, an
|
|
86
|
+
L{IExclusiveWorker} whose C{do} method ensures that its given work
|
|
87
|
+
will be executed in a mutually exclusive context, not in parallel
|
|
88
|
+
with other work enqueued by C{do} (although possibly in parallel
|
|
89
|
+
with the caller).
|
|
90
|
+
|
|
91
|
+
@param createWorker: A 0-argument callable that will create an
|
|
92
|
+
L{IWorker} to perform work.
|
|
93
|
+
|
|
94
|
+
@param logException: A 0-argument callable called in an exception
|
|
95
|
+
context when the work passed to C{do} raises an exception.
|
|
96
|
+
"""
|
|
97
|
+
self._quit = Quit()
|
|
98
|
+
self._coordinator = coordinator
|
|
99
|
+
self._createWorker = createWorker
|
|
100
|
+
self._logException = logException
|
|
101
|
+
|
|
102
|
+
# Don't touch these except from the coordinator.
|
|
103
|
+
self._idle: set[IWorker] = set()
|
|
104
|
+
self._busyCount = 0
|
|
105
|
+
self._pending: deque[Callable[..., object]] = deque()
|
|
106
|
+
self._shouldQuitCoordinator = False
|
|
107
|
+
self._toShrink = 0
|
|
108
|
+
|
|
109
|
+
def statistics(self) -> Statistics:
|
|
110
|
+
"""
|
|
111
|
+
Gather information on the current status of this L{Team}.
|
|
112
|
+
|
|
113
|
+
@return: a L{Statistics} describing the current state of this L{Team}.
|
|
114
|
+
"""
|
|
115
|
+
return Statistics(len(self._idle), self._busyCount, len(self._pending))
|
|
116
|
+
|
|
117
|
+
def grow(self, n: int) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Increase the the number of idle workers by C{n}.
|
|
120
|
+
|
|
121
|
+
@param n: The number of new idle workers to create.
|
|
122
|
+
@type n: L{int}
|
|
123
|
+
"""
|
|
124
|
+
self._quit.check()
|
|
125
|
+
|
|
126
|
+
@self._coordinator.do
|
|
127
|
+
def createOneWorker() -> None:
|
|
128
|
+
for x in range(n):
|
|
129
|
+
worker = self._createWorker()
|
|
130
|
+
if worker is None:
|
|
131
|
+
return
|
|
132
|
+
self._recycleWorker(worker)
|
|
133
|
+
|
|
134
|
+
def shrink(self, n: int | None = None) -> None:
|
|
135
|
+
"""
|
|
136
|
+
Decrease the number of idle workers by C{n}.
|
|
137
|
+
|
|
138
|
+
@param n: The number of idle workers to shut down, or L{None} (or
|
|
139
|
+
unspecified) to shut down all workers.
|
|
140
|
+
@type n: L{int} or L{None}
|
|
141
|
+
"""
|
|
142
|
+
self._quit.check()
|
|
143
|
+
self._coordinator.do(lambda: self._quitIdlers(n))
|
|
144
|
+
|
|
145
|
+
def _quitIdlers(self, n: int | None = None) -> None:
|
|
146
|
+
"""
|
|
147
|
+
The implmentation of C{shrink}, performed by the coordinator worker.
|
|
148
|
+
|
|
149
|
+
@param n: see L{Team.shrink}
|
|
150
|
+
"""
|
|
151
|
+
if n is None:
|
|
152
|
+
n = len(self._idle) + self._busyCount
|
|
153
|
+
for x in range(n):
|
|
154
|
+
if self._idle:
|
|
155
|
+
self._idle.pop().quit()
|
|
156
|
+
else:
|
|
157
|
+
self._toShrink += 1
|
|
158
|
+
if self._shouldQuitCoordinator and self._busyCount == 0:
|
|
159
|
+
self._coordinator.quit()
|
|
160
|
+
|
|
161
|
+
def do(self, task: Callable[[], object]) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Perform some work in a worker created by C{createWorker}.
|
|
164
|
+
|
|
165
|
+
@param task: the callable to run
|
|
166
|
+
"""
|
|
167
|
+
self._quit.check()
|
|
168
|
+
self._coordinator.do(lambda: self._coordinateThisTask(task))
|
|
169
|
+
|
|
170
|
+
def _coordinateThisTask(self, task: Callable[..., object]) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Select a worker to dispatch to, either an idle one or a new one, and
|
|
173
|
+
perform it.
|
|
174
|
+
|
|
175
|
+
This method should run on the coordinator worker.
|
|
176
|
+
|
|
177
|
+
@param task: the task to dispatch
|
|
178
|
+
@type task: 0-argument callable
|
|
179
|
+
"""
|
|
180
|
+
worker = self._idle.pop() if self._idle else self._createWorker()
|
|
181
|
+
if worker is None:
|
|
182
|
+
# The createWorker method may return None if we're out of resources
|
|
183
|
+
# to create workers.
|
|
184
|
+
self._pending.append(task)
|
|
185
|
+
return
|
|
186
|
+
not_none_worker = worker
|
|
187
|
+
self._busyCount += 1
|
|
188
|
+
|
|
189
|
+
@worker.do
|
|
190
|
+
def doWork() -> None:
|
|
191
|
+
try:
|
|
192
|
+
task()
|
|
193
|
+
except BaseException:
|
|
194
|
+
self._logException()
|
|
195
|
+
|
|
196
|
+
@self._coordinator.do
|
|
197
|
+
def idleAndPending() -> None:
|
|
198
|
+
self._busyCount -= 1
|
|
199
|
+
self._recycleWorker(not_none_worker)
|
|
200
|
+
|
|
201
|
+
def _recycleWorker(self, worker: IWorker) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Called only from coordinator.
|
|
204
|
+
|
|
205
|
+
Recycle the given worker into the idle pool.
|
|
206
|
+
|
|
207
|
+
@param worker: a worker created by C{createWorker} and now idle.
|
|
208
|
+
@type worker: L{IWorker}
|
|
209
|
+
"""
|
|
210
|
+
self._idle.add(worker)
|
|
211
|
+
if self._pending:
|
|
212
|
+
# Re-try the first enqueued thing.
|
|
213
|
+
# (Explicitly do _not_ honor _quit.)
|
|
214
|
+
self._coordinateThisTask(self._pending.popleft())
|
|
215
|
+
elif self._shouldQuitCoordinator:
|
|
216
|
+
self._quitIdlers()
|
|
217
|
+
elif self._toShrink > 0:
|
|
218
|
+
self._toShrink -= 1
|
|
219
|
+
self._idle.remove(worker)
|
|
220
|
+
worker.quit()
|
|
221
|
+
|
|
222
|
+
def quit(self) -> None:
|
|
223
|
+
"""
|
|
224
|
+
Stop doing work and shut down all idle workers.
|
|
225
|
+
"""
|
|
226
|
+
self._quit.set()
|
|
227
|
+
# In case all the workers are idle when we do this.
|
|
228
|
+
|
|
229
|
+
@self._coordinator.do
|
|
230
|
+
def startFinishing() -> None:
|
|
231
|
+
self._shouldQuitCoordinator = True
|
|
232
|
+
self._quitIdlers()
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# -*- test-case-name: slopped._threads.test.test_threadworker -*-
|
|
2
|
+
# Copyright (c) Slopped Matrix Laboratories.
|
|
3
|
+
# See LICENSE for details.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Implementation of an L{IWorker} based on native threads and queues.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
from enum import Enum, auto
|
|
12
|
+
from typing import TYPE_CHECKING, Callable, Literal, Protocol, TypeVar
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
import threading
|
|
16
|
+
|
|
17
|
+
from zope.interface import implementer
|
|
18
|
+
|
|
19
|
+
from ._convenience import Quit
|
|
20
|
+
from ._ithreads import IExclusiveWorker
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Stop(Enum):
|
|
24
|
+
Thread = auto()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
StopThread = Stop.Thread
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
U = TypeVar("U")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SimpleQueue(Protocol[T]):
|
|
34
|
+
def put(self, item: T) -> None:
|
|
35
|
+
...
|
|
36
|
+
|
|
37
|
+
def get(self) -> T:
|
|
38
|
+
...
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# when the sentinel value is a literal in a union, this is how iter works
|
|
42
|
+
smartiter: Callable[[Callable[[], T | U], U], Iterator[T]]
|
|
43
|
+
smartiter = iter # type:ignore[assignment]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@implementer(IExclusiveWorker)
|
|
47
|
+
class ThreadWorker:
|
|
48
|
+
"""
|
|
49
|
+
An L{IExclusiveWorker} implemented based on a single thread and a queue.
|
|
50
|
+
|
|
51
|
+
This worker ensures exclusivity (i.e. it is an L{IExclusiveWorker} and not
|
|
52
|
+
an L{IWorker}) by performing all of the work passed to C{do} on the I{same}
|
|
53
|
+
thread.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
startThread: Callable[[Callable[[], object]], object],
|
|
59
|
+
queue: SimpleQueue[Callable[[], object] | Literal[Stop.Thread]],
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
Create a L{ThreadWorker} with a function to start a thread and a queue
|
|
63
|
+
to use to communicate with that thread.
|
|
64
|
+
|
|
65
|
+
@param startThread: a callable that takes a callable to run in another
|
|
66
|
+
thread.
|
|
67
|
+
|
|
68
|
+
@param queue: A L{Queue} to use to give tasks to the thread created by
|
|
69
|
+
C{startThread}.
|
|
70
|
+
"""
|
|
71
|
+
self._q = queue
|
|
72
|
+
self._hasQuit = Quit()
|
|
73
|
+
|
|
74
|
+
def work() -> None:
|
|
75
|
+
for task in smartiter(queue.get, StopThread):
|
|
76
|
+
task()
|
|
77
|
+
|
|
78
|
+
startThread(work)
|
|
79
|
+
|
|
80
|
+
def do(self, task: Callable[[], None]) -> None:
|
|
81
|
+
"""
|
|
82
|
+
Perform the given task on the thread owned by this L{ThreadWorker}.
|
|
83
|
+
|
|
84
|
+
@param task: the function to call on a thread.
|
|
85
|
+
"""
|
|
86
|
+
self._hasQuit.check()
|
|
87
|
+
self._q.put(task)
|
|
88
|
+
|
|
89
|
+
def quit(self) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Reject all future work and stop the thread started by C{__init__}.
|
|
92
|
+
"""
|
|
93
|
+
# Reject all future work. Set this _before_ enqueueing _stop, so
|
|
94
|
+
# that no work is ever enqueued _after_ _stop.
|
|
95
|
+
self._hasQuit.set()
|
|
96
|
+
self._q.put(StopThread)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class SimpleLock(Protocol):
|
|
100
|
+
def acquire(self) -> bool:
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
def release(self) -> None:
|
|
104
|
+
...
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@implementer(IExclusiveWorker)
|
|
108
|
+
class LockWorker:
|
|
109
|
+
"""
|
|
110
|
+
An L{IWorker} implemented based on a mutual-exclusion lock.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, lock: SimpleLock, local: threading.local):
|
|
114
|
+
"""
|
|
115
|
+
@param lock: A mutual-exclusion lock, with C{acquire} and C{release}
|
|
116
|
+
methods.
|
|
117
|
+
@type lock: L{threading.Lock}
|
|
118
|
+
|
|
119
|
+
@param local: Local storage.
|
|
120
|
+
@type local: L{threading.local}
|
|
121
|
+
"""
|
|
122
|
+
self._quit = Quit()
|
|
123
|
+
self._lock: SimpleLock | None = lock
|
|
124
|
+
self._local = local
|
|
125
|
+
|
|
126
|
+
def do(self, work: Callable[[], None]) -> None:
|
|
127
|
+
"""
|
|
128
|
+
Do the given work on this thread, with the mutex acquired. If this is
|
|
129
|
+
called re-entrantly, return and wait for the outer invocation to do the
|
|
130
|
+
work.
|
|
131
|
+
|
|
132
|
+
@param work: the work to do with the lock held.
|
|
133
|
+
"""
|
|
134
|
+
lock = self._lock
|
|
135
|
+
local = self._local
|
|
136
|
+
self._quit.check()
|
|
137
|
+
working = getattr(local, "working", None)
|
|
138
|
+
if working is None:
|
|
139
|
+
assert lock is not None, "LockWorker used after quit()"
|
|
140
|
+
working = local.working = []
|
|
141
|
+
working.append(work)
|
|
142
|
+
lock.acquire()
|
|
143
|
+
try:
|
|
144
|
+
while working:
|
|
145
|
+
working.pop(0)()
|
|
146
|
+
finally:
|
|
147
|
+
lock.release()
|
|
148
|
+
local.working = None
|
|
149
|
+
else:
|
|
150
|
+
working.append(work)
|
|
151
|
+
|
|
152
|
+
def quit(self) -> None:
|
|
153
|
+
"""
|
|
154
|
+
Quit this L{LockWorker}.
|
|
155
|
+
"""
|
|
156
|
+
self._quit.set()
|
|
157
|
+
self._lock = None
|