@@ -72,6 +72,7 @@ def _path_importer_cache(cls, path):
7272import os
7373import pstats
7474import pty
75+ import re
7576import signal
7677import socket
7778import struct
@@ -360,6 +361,18 @@ class LatchError(Error):
360361 pass
361362
362363
364+ class ModuleDeniedByOverridesError (ModuleNotFoundError ):
365+ fmt = "Mitogen won't serve %s, it's not in the overrides list"
366+
367+
368+ class ModuleDeniedByBlocksError (ModuleNotFoundError ):
369+ fmt = "Mitogen won't serve %s, it's in the blocks list"
370+
371+
372+ class ModuleUnsuitableError (ModuleNotFoundError ):
373+ fmt = "Mitogen won't serve %s, it's e.g. binary, legacy, part of stdlib"
374+
375+
363376class Blob (BytesType ):
364377 """
365378 A serializable bytes subclass whose content is summarized in repr() output,
@@ -499,6 +512,11 @@ def has_parent_authority(msg, _stream=None):
499512 return _has_parent_authority (msg .auth_id )
500513
501514
515+ def module_lineage (fullname ):
516+ "Return an iterator of a module's parent fullnames and its own"
517+ return (fullname [:m .start ()] for m in re .finditer (r'\.|\Z' , fullname ))
518+
519+
502520def _signals (obj , signal ):
503521 return (
504522 obj .__dict__
@@ -564,21 +582,6 @@ def takes_router(func):
564582 return func
565583
566584
567- def is_blacklisted_import (importer , fullname ):
568- """
569- Return :data:`True` if `fullname` is part of a blacklisted package, or if
570- any packages have been whitelisted and `fullname` is not part of one.
571-
572- NB:
573- - The default whitelist is `['']` which matches any module name.
574- - If a package is on both lists, then it is treated as blacklisted.
575- - If any package is whitelisted, then all non-whitelisted packages are
576- treated as blacklisted.
577- """
578- return ((not any (fullname .startswith (s ) for s in importer .whitelist )) or
579- (any (fullname .startswith (s ) for s in importer .blacklist )))
580-
581-
582585def set_cloexec (fd ):
583586 """
584587 Set the file descriptor `fd` to automatically close on :func:`os.execve`.
@@ -1302,6 +1305,46 @@ def __repr__(self):
13021305 )
13031306
13041307
1308+ class ImportPolicy (object ):
1309+ """
1310+ Policy deciding which module prefixes :class:`Importer` will request from
1311+ :class:`mitogen.master.ModuleResponder` and which requests will be served
1312+ or denied.
1313+
1314+ :param overrides:
1315+ Prefixes always requested, ignoring local versions. If ``overrides``
1316+ has entries, then it's also used as an allow list by the responder -
1317+ any request for a prefix that's not overriden will be denied.
1318+
1319+ :param blocks:
1320+ Prefixes always denied by the responder, only local versions can be
1321+ used.
1322+ """
1323+ def __init__ (self , overrides = (), blocks = ()):
1324+ self .overrides = set (overrides )
1325+ self .blocks = set (blocks )
1326+ self ._always = set (Importer .ALWAYS_BLACKLIST )
1327+
1328+ def denied (self , fullname ):
1329+ fullnames = frozenset (module_lineage (fullname ))
1330+ if self .overrides and not self .overrides .intersection (fullnames ):
1331+ return ModuleDeniedByOverridesError
1332+ if self .blocks .intersection (fullnames ): return ModuleDeniedByBlocksError
1333+ if self ._always .intersection (fullnames ): return ModuleUnsuitableError
1334+ return False
1335+
1336+ def denied_raise (self , fullname ):
1337+ denial = self .denied (fullname )
1338+ if denial : raise denial (denial .fmt % (fullname ,))
1339+
1340+ def overriden (self , fullname ):
1341+ return bool (self .overrides .intersection (module_lineage (fullname )))
1342+
1343+ def __repr__ (self ):
1344+ args = (type (self ).__name__ , self .overrides , self .blocks )
1345+ return '%s(overrides=%r, blocks=%r)' % args
1346+
1347+
13051348class Importer (object ):
13061349 """
13071350 Import protocol implementation that fetches modules from the parent
@@ -1361,18 +1404,12 @@ class Importer(object):
13611404 if sys .version_info >= (3 , 0 ):
13621405 ALWAYS_BLACKLIST += ['cStringIO' ]
13631406
1364- def __init__ (self , router , context , core_src , whitelist = (), blacklist = () ):
1407+ def __init__ (self , router , context , core_src , policy ):
13651408 self ._log = logging .getLogger ('mitogen.importer' )
13661409 self ._context = context
13671410 self ._present = {'mitogen' : self .MITOGEN_PKG_CONTENT }
13681411 self ._lock = threading .Lock ()
1369- self .whitelist = list (whitelist ) or ['' ]
1370- self .blacklist = list (blacklist ) + self .ALWAYS_BLACKLIST
1371-
1372- # Preserve copies of the original server-supplied whitelist/blacklist
1373- # for later use by children.
1374- self .master_whitelist = self .whitelist [:]
1375- self .master_blacklist = self .blacklist [:]
1412+ self .policy = policy
13761413
13771414 # Presence of an entry in this map indicates in-flight GET_MODULE.
13781415 self ._callbacks = {}
@@ -1465,11 +1502,8 @@ def find_module(self, fullname, path=None):
14651502 self ._log .debug ('%s has no submodule %s' , pkgname , suffix )
14661503 return None
14671504
1468- # #114: explicitly whitelisted prefixes override any
1469- # system-installed package.
1470- if self .whitelist != ['' ]:
1471- if any (fullname .startswith (s ) for s in self .whitelist ):
1472- return self
1505+ if self .policy .overriden (fullname ):
1506+ return self
14731507
14741508 try :
14751509 self .builtin_find_module (fullname )
@@ -1515,11 +1549,9 @@ def find_spec(self, fullname, path, target=None):
15151549 fullname , pkgname , pkg_loader )
15161550 return None
15171551
1518- # #114: whitelisted prefixes override any system-installed package.
1519- if self .whitelist != ['' ]:
1520- if any (s and fullname .startswith (s ) for s in self .whitelist ):
1521- log .debug ('Handling %s. It is whitelisted' , fullname )
1522- return importlib .machinery .ModuleSpec (fullname , loader = self )
1552+ if self .policy .overriden (fullname ):
1553+ log .debug ('Handling %s. It is overriden' , fullname )
1554+ return importlib .machinery .ModuleSpec (fullname , loader = self )
15231555
15241556 if fullname == '__main__' :
15251557 log .debug ('Handling %s. A special case' , fullname )
@@ -1540,10 +1572,6 @@ def find_spec(self, fullname, path, target=None):
15401572 log .debug ('Handling %s. Unavailable locally' , fullname )
15411573 return importlib .machinery .ModuleSpec (fullname , loader = self )
15421574
1543- blacklisted_msg = (
1544- 'A %r request would be refused by the Mitogen master. The module is '
1545- 'on the deny list (blacklist) or not on the allow list (whitelist).'
1546- )
15471575 pkg_resources_msg = (
15481576 'pkg_resources is prohibited from importing __main__, as it causes '
15491577 'problems in applications whose main module is not designed to be '
@@ -1556,8 +1584,7 @@ def find_spec(self, fullname, path, target=None):
15561584 )
15571585
15581586 def _refuse_imports (self , fullname ):
1559- if is_blacklisted_import (self , fullname ):
1560- raise ModuleNotFoundError (self .blacklisted_msg % (fullname ,))
1587+ self .policy .denied_raise (fullname )
15611588
15621589 f = sys ._getframe (2 )
15631590 requestee = f .f_globals ['__name__' ]
@@ -4175,12 +4202,15 @@ def _setup_importer(self):
41754202 else :
41764203 core_src = None
41774204
4205+ policy = ImportPolicy (
4206+ self .config ['import_overrides' ],
4207+ self .config ['import_blocks' ],
4208+ )
41784209 importer = Importer (
41794210 self .router ,
41804211 self .parent ,
41814212 core_src ,
4182- self .config .get ('whitelist' , ()),
4183- self .config .get ('blacklist' , ()),
4213+ policy ,
41844214 )
41854215
41864216 self .importer = importer
0 commit comments