I'm currently working on a CLI abstraction layer, which abstracts CLI programs as classes in Python. Such CLI programs offer a structured way to enable and configure CLI parameters. It helps checking for faulty inputs and generated properly escaped arguments (e.g. adding double quotes).
Note: The following example is using Git, while in my target application, it will be commercial tools, that don't offer any Python API or similar.
Basic Ideas:
Git
class, which derives from class Program
.Git
class.CLIOption
derived from Attribute
Git
is used to enabled / configure CLI parameters and helps to assemble a list of correctly encoded strings that can be used e.g. in subprocess.Popen(...)
tool = Git()
tool[tool.FlagVersion] = True
print(tool.ToArgumentList())
Some Python Code:
from pyAttributes import Attribute
class CLIOption(Attribute): ... # see pyAttributes for more details
class Argument:
_name: ClassVar[str]
def __init_subclass__(cls, *args, name: str = "", **kwargs):
super().__init_subclass__(*args, **kwargs)
cls._name = name
class FlagArgument(Argument): ...
class CommandArgument(Argument): ...
class Program:
__cliOptions__: Dict[Type[Argument], Optional[Argument]]
def __init_subclass__(cls, *args, **kwargs):
"""Hook into subclass creation to register all marked CLI options in ``__cliOptions__``.
super().__init_subclass__(*args, **kwargs)
# get all marked options and
cls.__cliOptions__ = {}
for option in CLIOption.GetClasses():
cls.__cliOptions__[option] = None
class Git(Program):
@CLIOption()
class FlagVersion(FlagArgument, name="--version"): ...
@CLIOption()
class FlagHelp(FlagArgument, name="--help"): ...
@CLIOption()
class CmdCommit(CommandArgument, name="commit"): ...
Observations:
FlagVersion
flag. Some as -v
, but others as --version
.Primary Questions:
FlagVersion
is a nested class of class Git
?What I investigated so far:
isinstance(...)
or issubclass(...)
are offering.__module__
reference to the outer scope, nested classes have no "pointer" to the next outer scope.__module__
values.__qualname__
includes the names of parent classes.Git.FlagVersion
So I see a possible "ugly" solution using __qualname__
and string operations to check if a class is nested and if it's nested in a certain outer scope.
Algorithm:
__module__
and __qualname__
.This gets even more complicated if one nested class is defined in a parent class and another nested class is defined in a derived class. Then I also need to look into MRO ... oOo
Secondary Questions:
Following the proposed approaches by iterating __dict__
works quite good.
So this was the first solution developed based on the given ideas:
def isnestedclass(cls: Type, scope: Type) -> bool:
for memberName in scope.__dict__:
member = getattr(scope, memberName)
if type(member) is type:
if cls is member:
return True
return False
That solution doesn't work on members inherited from parent classes.
So I extended it with searching through the inheritance graph via mro()
.
This is my current and final solution for a isnestedclass
helper function.
def isnestedclass(cls: Type, scope: Type) -> bool:
for mroClass in scope.mro():
for memberName in mroClass.__dict__:
member = getattr(mroClass, memberName)
if type(member) is type:
if cls is member:
return True
return False
The function is available within the pyTooling package.