def edit(self, filename): """ Edit a file using the configured text editor. """ #FIXME: edit by calling the locator and manage its local file (e.g. for a URL, point to a temp folder) ted = self.console.config['TEXT_EDITOR'] if which(ted) is None: raise ValueError("'%s' does not exist or is not installed" % ted) p = Path(self.console.config['WORKSPACE']).joinpath(filename) if not p.exists(): p.touch() call([ted, str(p)], stderr=PIPE)
def load_entities(entities, *sources, **kwargs): """ Load every entity class of the given type found in the given source folders. :param sources: paths (either with ~, relative or absolute) to folders containing entity subclasses :param include_base: include the base entities provided with the package :param select: selected modules in the source folder :param exclude: list of entity identifiers (in custom format, or simply the entity class) to be excluded (useful when including the base but not every entity is required) :param backref: list of attrs to get entity's class to be bound to :param docstr_parser: user-defined docstring parser for populating metadata """ global ENTITIES ENTITIES = [e.__name__ for e in entities] sources = list(sources) if kwargs.get("include_base", True): # this allows to use sploitkit.base for starting a project with a baseline of entities for n in ENTITIES: n = n.lower() for m in kwargs.get("select", {}).get(n, [""]): m = "../base/{}s/".format(n) + m + [".py", ""][m == ""] p = Path(__file__).parent.joinpath(m).resolve() if p.exists(): sources.insert(0, p) # load every single source (folder of modules or single module) for s in sources: if not s.exists(): logger.debug("Source file does not exist: %s" % s) continue # bind the source to the entity main class for e in entities: e._source = str(s) # now, it loads every Python module from the list of source folders ; when loading entity subclasses, these are # registered to entity's registry for further use (i.e. from the console) logger.debug("Loading Python source: %s" % s) PythonPath(s) for e in entities: tbr = [] # clean up the temporary attribute if hasattr(e, "_source"): delattr(e, "_source") # remove proxy classes n = e.__name__.lower() for c in e.subclasses[:]: if len(c.__subclasses__()) > 0: getattr(e, "unregister_%s" % n, Entity.unregister_subclass)(c) # handle specific entities or sets of entities exclusions ; this will remove them from Entity's registries excludes = kwargs.get("exclude", {}).get(n) if excludes is not None: getattr(e, "unregister_%ss" % n, Entity.unregister_subclasses)(*excludes) # handle conditional entities ; this will remove entities having a "condition" method returning False for c in e.subclasses[:]: # convention: conditional entities are unregistered and removed if hasattr(c, "condition") and not c().condition(): getattr(e, "unregister_%s" % n, Entity.unregister_subclass)(c) # now populate metadata for each class for c in e.subclasses: set_metadata(c, kwargs.get("docstr_parser", lambda s: {})) # bind entity's subclasses to the given attributes for back-reference backrefs = kwargs.get("backref", {}).get(n) if backrefs is not None: for c in e.subclasses: for br in backrefs: try: a, bn = br # [a]ttribute, [b]ackref [n]ame except ValueError: a, bn = None, br[0] if isinstance(br, tuple) else br bc = list(filter(lambda _: _.__name__.lower() == bn, entities))[0] # [b]ackref [c]lass if a and getattr(c, a, None): c = getattr(c, a) setattr(c, bn, lambda: bc._instance) # then trigger garbage collection (for removed classes) gc.collect()