Base Crawler¶
pcapkit.vendor.default exports Vendor
only, the base class of every vendor crawler.
- class pcapkit.vendor.default.Vendor[source]¶
Bases:
objectDefault vendor generator.
Inherit this class with
FLAG&LINKattributes, etc., to implement a new vendor generator.- PROTO_DEST: tuple[ModuleDescriptor[Any], ...] = ()¶
Protocol class(es) owning the
__proto__dispatch registry keyed by the generated enumeration, rendered into its class body as__proto_dest__– the declarationregister_protocol_code()reads to resolve a bare member of that enumeration into its destination(s). Asked for in review of #2392; no issue holds the ruling.Only the registries that key a
__proto__declare one, and the template renders nothing for a crawler that leaves this empty, so every other generated module is unaffected. A class is named as a lazyModuleDescriptorrather than imported, becausepcapkit.constsits belowpcapkit.protocols.Added in version 1.5.0.
- static wrap_comment(text)[source]¶
Wraps long-length text to shorter lines of comments.
Sphinx renders a
#:comment as reST, and its continuation lines join into one paragraph, so the text is made safe for that first:a space goes after a URL reference that another bracket follows directly, as in
[http://ele2.io][Damir Franusic]. docutils reads]and[as URL characters, so the autolink would run on into the next citation;a leading enumerator, such as the
(S)of(S) flag, is backslash-escaped, since reST would render it as an enumerated list.
A line never breaks at a hyphen, which would render
run-timeasrun- time. Only a word longer than a whole line is split inside.
- rename(name, code, *, original=None)[source]¶
Rename duplicated fields.
- Parameters:
- Return type:
- Returns:
Revised field name.
Example
If
namehas multiple occurrences in the source registry, the field name will be sanitised as${name}_${code}.Otherwise, the plain
namewill be returned.
- _request()[source]¶
Fetch CSV data from
LINK.This is the low-level call of
request().If
LINKisNone, it will directly call the upper methodrequest()with NO arguments.The method will first try to GET the content of
LINK. Should any exception be raised, it will first try with proxy settings fromget_proxies().Note
Since some
LINKlinks are from Wikipedia, etc., they might not be available in certain areas, e.g. the amazing PRC :)Should the proxies fail again, it will prompt for user intervention, i.e. it will use
webbrowser.open()to open the page in browser for you, and you can manually load that page and save the HTML source at the location it provides.That last resort is only taken when somebody can actually act on it, i.e. when
PCAPKIT_CI_MODEis unset andstdin_is_interactive()finds a terminal. A non-interactive run – under a pipe, in a container, from a scheduled job – is failed with the fetch error instead, the same wayPCAPKIT_CI_MODEfails it, rather than printing instructions nobody will read. See #522.- Return type:
- Returns:
CSV data.
- Raises:
requests.RequestException – If the registry could not be fetched and manual intervention is unavailable, i.e. under
PCAPKIT_CI_MODE, with no interactivestdin, or where the prompt itself fails becausestdinwent away mid-wait.- Warns:
VendorRequestWarning– If connection failed with and/or without proxies.
See also
- classmethod _dest_path()[source]¶
Resolve the
const/file this crawler’s module mirrors.__module__sits somewhere under thepcapkit.vendorpackage, at any depth – e.g.pcapkit.vendor.reg.linktypeone level down, orpcapkit.vendor.reg.apptype.tcp(#732) two. The output file mirrors that same position underpcapkit.const.The position is taken relative to the
vendorpackage’s own__file__rather than by splitting the module’s path a fixed number of times, so any nesting works: whatever path a module sits at relative tovendor/, the same relative path is mirrored underconst/. A fixed two-level split would sendvendor/reg/apptype/tcp.pytovendor/const/apptype/tcp.py, insidevendor/itself rather than underpcapkit/const/.pcapkit.vendoris imported here, inside the method, rather than at module scope: it imports this module while it is still initialising (seepcapkit/vendor/__init__.py), so a module-level import would have to reason about that partial state. By the time any concreteVendorsubclass is instantiated,pcapkit.vendorhas always finished importing.That relative-path mirroring is only safe once the module is confirmed to actually sit under
vendor_root–const/andvendor/are siblings at equal depth, so for a modulemthat is not undervendor_root,os.path.relpath(m, vendor_root)starts with enough..segments that re-joining them underconst_rootcancels back out tomitself. That is reachable: every crawler endssys.exit(SomeCrawler()), so running one from a second checkout, or from a module that never sits undervendor/at all, resolvespcapkit.vendorfrom wherever it is installed whileinspect.getfile(cls)names a file elsewhere entirely – andVendor.__init__then opens that path with'w', silently truncating whatever__module__actually names instead of raising. So the escape is rejected outright, rather than trusted to produce a harmless-looking wrong path.This is a classmethod, not an instance method, deliberately: the body needs no instance state, only the crawler’s class, which
clsis with no instance required to obtain it. That lets a caller resolve a crawler’s destination without instantiating it, and so without triggeringVendor.__init__()’s network fetch, render and write – the pre-run snapshot thatrun()inpcapkit.vendor.__main__takes (#872) is such a caller. No crawler overrides_dest_path, so the classmethod is authoritative for all of them.- Return type:
- Returns:
Absolute path of the constant module this crawler should write.
- Raises:
VendorPathNotFound – If this crawler’s own module is not located under the
pcapkit.vendorpackage root, so mirroring it underpcapkit.constcannot be done safely.
Running a Crawler¶
Instantiating a crawler fetches its registry and writes the constant module:
flowchart TD
NEW["a crawler instantiated: Vendor.\_\_init\_\_"]
L{"LINK set?"}
G["GET LINK<br/>a non-OK or empty page retried,<br/>up to MAX\_RETRY"]
P{"get\_proxies() gives any?"}
GP["GET LINK through the proxies<br/>retried the same way"]
CI{"PCAPKIT\_CI\_MODE on?"}
TTY{"stdin interactive?"}
RAISE["the request error is raised"]
MAN["manual: LINK opened in a browser,<br/>wait for its saved page source"]
REQ["request(text): the registry's records"]
CNT["count(data)"]
CTX["context(data): process(), then the module template"]
DEST["\_dest\_path(): the matching module under pcapkit/const/"]
WR["the constant module written"]
NEW --> L
L -->|"no: request() with no text"| REQ
L -->|yes| G
G -->|ok| REQ
G -->|fails| P
P -->|yes| GP
P -->|no| CI
GP -->|ok| REQ
GP -->|fails| CI
CI -->|yes| RAISE
CI -->|no| TTY
TTY -->|no| RAISE
TTY -->|yes| MAN --> REQ
REQ --> CNT --> CTX --> DEST --> WR
click NEW "default.html#pcapkit.vendor.default.Vendor.__init__"
click REQ "default.html#pcapkit.vendor.default.Vendor.request"
click CNT "default.html#pcapkit.vendor.default.Vendor.count"
click CTX "default.html#pcapkit.vendor.default.Vendor.context"
click DEST "default.html#pcapkit.vendor.default.Vendor._dest_path"
Each failed fetch warns with VendorRequestWarning.
Environment Variables¶
The crawlers’ variables are read here, not by pcapkit.utilities.config
(#1726). PCAPKIT_VENDOR_RETRY and PCAPKIT_CI_MODE
are read once, when this module is imported, with
read_limit() and
read_flag(): a bad value warns with a
ConfigWarning, naming the variable and the
value, and falls back to the default. The proxy addresses are read by
get_proxies() on every call, so a proxy set after
the import is still honoured.
- PCAPKIT_HTTP_PROXY¶
The crawlers’ HTTP proxy address; unset by default.
- PCAPKIT_HTTPS_PROXY¶
The crawlers’ HTTPS proxy address; unset by default.
- pcapkit.vendor.default.MAX_RETRY = 5¶
How many times
Vendor._request()asks for a crawler’s page, both directly and then through the proxies ofget_proxies(), before giving up,PCAPKIT_VENDOR_RETRY.Changed in version 1.5.0: Read with
read_limit()(#1726). A value that is not an integer of at least1warns withConfigWarningand falls back to5, where one that was not an integer crashed the import, and0was read as1.- Type:
- pcapkit.vendor.default.CI_MODE = False¶
Whether the crawlers run in CI, where a failed fetch fails the run instead of prompting,
PCAPKIT_CI_MODE.Changed in version 1.5.0: Read with
read_flag()(#1726). A value it does not accept warns withConfigWarningand falls back to off, where it was taken as off silently.- Type:
Internal Definitions¶
- class pcapkit.vendor.default.VendorMeta(name, bases, namespace, /, **kwargs)[source]¶
Bases:
ABCMetaMeta class to add dynamic support to
Vendor.It defines nothing of its own, so a crawler class is built exactly as by
abc.ABCMeta.
- pcapkit.vendor.default.LINE(NAME, DOCS, FLAG, ENUM, MISS, MODL, DEST=())¶
Default constant template of enumeration registry from IANA CSV.
- Parameters:
NAME (str) – name of the constant enumeration class
DOCS (str) – docstring for the constant enumeration class
FLAG (str) – threshold value validator (range of valid values)
ENUM (str) – enumeration data (class attributes)
MISS (str) – missing value handler (default value)
MODL (str) – module name of the constant enumeration class
DEST (tuple[ModuleDescriptor, ...]) – the crawler’s
PROTO_DEST, rendered as the class’s__proto_dest__; nothing is rendered when it is empty, which is the case for every registry that keys no__proto__
- Return type:
- pcapkit.vendor.default.PROTO_DEST_IMPORT = 'from pcapkit.corekit.module import ModuleDescriptor\n'¶
Import line the generated module needs for a non-empty
__proto_dest__.LINErenders it only when the crawler declaresVendor.PROTO_DEST, so an enumeration naming no destination keeps the import block it had. The trailing newline lives here rather than in the f-string because an f-string replacement field may not contain a backslash before Python 3.12, and this package still builds on 3.8.
- pcapkit.vendor.default.render_proto_dest(descriptors)[source]¶
Render the generated class’s
__proto_dest__declaration.LINEcalls this with the crawler’sVendor.PROTO_DEST. It is a function rather than an expression inside the f-string for the reasonPROTO_DEST_IMPORTgives: the block it returns contains newlines, which an f-string replacement field cannot spell before Python 3.12.- Parameters:
descriptors (
tuple[ModuleDescriptor[Any],...]) – The crawler’sVendor.PROTO_DEST.- Return type:
- Returns:
The declaration, indented as a class body and followed by a blank line, or the empty string when
descriptorsis empty – which is what keeps the template’s output byte-identical for every enumeration that names no destination.
- pcapkit.vendor.default.get_proxies()[source]¶
Get proxy for blocked sites.
The function will read
PCAPKIT_HTTP_PROXYandPCAPKIT_HTTPS_PROXY, if any, on every call, for the proxy settings ofrequests.
- pcapkit.vendor.default.stdin_is_interactive()[source]¶
Whether there is somebody at a keyboard to be prompted.
Vendor._request()’s last resort is to ask an operator to fetch the page by hand, which needs a terminal: a process whosestdinis a pipe, a file, or/dev/nullhas nobody to open a browser for and nobody who could ever save the file that path then waits for.stdincan be absent rather than merely redirected, and it can stop being usable while the process runs, so neither is allowed to propagate out of a predicate:sys.stdinisNoneunder a GUI launcher such as pythonw, and one that has been closed raisesValueErrorfromisatty()instead of answering. Every such case counts as not interactive, which is the conservative answer – it costs a crawler the manual-intervention path it could not have used anyway.