How to introspect a Click application? - python

I have a Click app called "DC" starting with click.Group cli(), which has many subcommands. I'm trying to produce a text file with a list of all commands, arguments, options, and help text as a convenient reference. How do I introspect a Click application?
I experimented using the API reference, but it's confusing. Some of Command's methods (like get_usage()) require a "context" object as the first arg, and I only know two ways to get one:
Be inside a command with #pass_context decorator. (Not always the case.)
Call click.get_current_context() to get the "current" context, which seems to be the one attached to the bottom-level command that is currently being executed.
That seemed to work:
from dc.__main__ import cli
current_ctx = click.get_current_context()
click.echo(cli.get_usage(current_ctx))
This prints the docstring from the cli() function. However, if I try to inspect the list of subcommands:
click.echo(cli.commands)
I get an empty dict. After more exploring, I finally managed to find my way to the real data by doing this:
current_ctx.find_root().command.commands
which returned a dict with all the top-level commands I expected to see.
Is that the preferred method?

Related

Select Directory with Python

I want to select 5 images with Python so that I can use these imges in my python program. I tried to do this with QFileDialog() (PyQt5) but I only succeed to single select a file.
And how to select a folder is also not really comprehensive.
I just want to select 5 images and select a folder so that I can save files in that folder. But it seems to be not so easy to do that.
I really like Python because its so easy but PyQt5 makes me everytime I use it just aggressive, all other libraries are just nice and easy to understand.
Maybe there is a good alternative to pyqt? tkinter maybe?
thanks.
In order to select a folder you can use this code:
widget = QWidget()
dialog = QFileDialog(
widget, "Select Directory of the Desired Files", os.path.curdir
)
dialog.setFileMode(QFileDialog.DirectoryOnly)
Qt supplies a bunch of static methods to get standardized file dialogs, two of them already satisfy your needs: getOpenFileNames() (stress on the final "s") and getExistingDirectory().
The first will return a list of absolute paths of selected file[s], the last will return the selected directory.
I know that reading the official documentation might be a bit overwhelming if you don't know anything about C++ (they are explained in detail, though), but they're not as hard as one could think.
Every function is listed in a very simple way:
returned_type : function_name(arguments) [const -> you can usually ignore this]
The returned_type is the type of the value the function is expected to return. In "c++ slang", void is the same as return (or return None or no return at all, as Python implicitly returns None if no other value/object is returned at the end of a function), if the type is a QString it's automatically converted to a Python str, while qreal is the same as Python's floats,. This is very important for "private" functions (methods), which are internally used by Qt: if you are subclassing and want to override a private method of a Qt class, you have to return the type Qt expects. You could theoretically ignore the returned_type for public functions if you know what you're doing, but it's usually better to stick with the original type.
There are some small "exceptions" that require some consideration. In some cases Qt expects some argument that will be modified within the function and would normally return whether the function was successful or not, while in Python it might return the reference to the argument (sorry, I can't remember them right now). Some other functions return a tuple instead of a single value, and that's the case of some static QFileDialog functions such as getOpenFileName[s] which return both the selected file[s] and the selected filter.

python get members of COM ole object

In python 3.6 I'm using an COM interface to communicate with Excel and Word, in this case Word for automated reporting as the data processing is done in python.
I don't know how python can get the members of such a COM object similar to the use of the dir() function.
(Previously using Matlab, i would use the .get or .invoke methods to get this)
So the code would be:
def wordOpen(wordfile):
pythoncom.CoInitializeEx(pythoncom.COINIT_APARTMENTTHREADED)
wApp = win32com.client.DispatchEx('Word.Application')
wDoc = wApp.Documents.Open(wordfile)
wApp.Visible = 1
wApp.Activate
wRange = wDoc.Content
return wApp, wDoc, wRange
wApp, wDoc, wRange = wordOpen(wordfile)
dir(wDoc)
.. which does not provide me the list of methods and properties of the Word document object (similar for wApp and wRange).
Similarly I've tried inspect.getmembers(wDoc) but this also does not provide the list of methods/properties that I'm looking for.
For the same thing when communicating with Excel, I used to go into the VBA editor and get a list there, but is there any method to do this from the IPython
console directly?
Did some more searching and figured it out myself. The above example uses 'dynamic dispatching' and we want to use 'static dispatching' instead. That does not change the code, it only entails a one-off action.
Dynamic is a quick-and-dirty way to create these objects, and then python knows nothing about the type of object.
Static means that you have to run makepy.py from the command line to create a type library (in this case for Word objects), and after that has been done once, python knows all the info about the object every next time it launches the COM object.
The procedure is very simple and comparable to the VBA action of adding References to your project.

Python Click command names

I'm using the click package for creating a command line tool. However, I would like to have a 'list' command. For example:
#click.command
#click.option(help='list...')
def list():
# do stuff here
Is there another way in click to pass in a command name other than having it as the function name? I don't want this function to shadow python's built in list. I've looked through the documentation and can't really find anything about command names -- I've read up on command aliases but that doesn't seem to help this problem. Or do I not need to worry about list being shadowed since it's being wrapped by the click decorator? Thanks in advance.
You can provide the name argument when you use the command decorator. Once you've done that, you can name your function whatever you want:
#click.command(name='list')
def list_command():
pass
See the Click documentation for details.

How do I register a mark in pytest 2.5.1?

I've the read pytest documentation. Section 7.4.3 gives instructions for registering markers. I have followed the instructions exactly, but it doesn't seem to have worked for me.
I'm using Python 2.7.2 and pytest 2.5.1.
I have a pytest.ini file at the root of my project. Here is the entire contents of that file:
[pytest]
python_files=*.py
python_classes=Check
python_functions=test
rsyncdirs = . logs
rsyncignore = docs archive third_party .git procs
markers =
mammoth: mark a test as part of the Mammoth regression suite
A little background to give context: The folks that created the automation framework I am working on no longer work for the company. They created a custom plugin that extended the functionality of the default pytest.mark. From what I understand, the only thing the custom plugin does is make it so that I can add marks to a test like this:
#pytest.marks(CompeteMarks.MAMMOTH, CompeteMarks.QUICK_TEST_A, CompeteMarks.PROD_BVT)
def my_test(self):
instead of like this:
#pytest.mark.mammoth
#pytest.mark.quick_test_a
#pytest.mark.prod_bvt
def my_test(self):
The custom plugin code remains present in the code base. I do not know if that has any negative effect on trying to register a mark, but thought it was worth mentioning if someone knows otherwise.
The problem I'm having is when I execute the following command on a command-line, I do NOT see my mammoth mark listed among the other registered marks.
py.test --markers
The output returned after running the above command is this:
#pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
#pytest.mark.xfail(condition, reason=None, run=True): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. See http://pytest.org/latest/skipping.html
#pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: #parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
#pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
#pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
#pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible.
What am I doing wrong and how can I get my mark registered?
One more piece of info, I have applied the mammoth mark to a single test (shown below) when I ran the py.test --markers command:
#pytest.mark.mammoth
def my_test(self):
If I understand your comments correctly the project layout is the following:
~/customersites/
~/customersites/automation/
~/customersites/automation/pytest.ini
Then invoking py.test as follows:
~/customersites$ py.test --markers
will make py.test look for a configuration file in ~/customersites/ and subsequently all the parents: ~/, /home/, /. In this case this will not make it find pytest.ini.
However when you invoke it with one or more arguments, py.test will try to interpret each argument as a file or directory and start looking for a configuration file from that directory and it's parents. It then iterates through all arguments in order until it found the first configuration file.
So with the above directory layout invoking py.test as follows will make it find pytest.ini and show the markers registered in it:
~/customersites$ py.test automation --markers
as now py.test will first look in ~/customersites/automation/ for a configuration file before going up the directory tree and looking in ~/customersites/. But since it finds one in ~/customersites/automation/pytest.ini it stops there and uses that.
Have you tried here?
From the docs:
API reference for mark related objects
class MarkGenerator[source]
Factory for MarkDecorator objects - exposed as a pytest.mark singleton
instance.
Example:
import py
#pytest.mark.slowtest
def test_function():
pass
will set a slowtest MarkInfo object on the test_function object.
class MarkDecorator(name, args=None, kwargs=None)[source]
A decorator for test functions and test classes. When applied it will
create MarkInfo objects which may be retrieved by hooks as item keywords.
MarkDecorator instances are often created like this:
mark1 = pytest.mark.NAME # simple MarkDecorator
mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
and can then be applied as decorators to test functions:
#mark2
def test_function():
pass

Python dir command to determine the methods

I am using Python's dir() function to determine what attributes and methods a class has.
For example to determine the methods in wx.Frame, I use dir(wx.Frame)
Is there any command to determine the list of arguments for each method? For example, if I want to know what arguments belong to wx.Frame.CreateToolBar().
As mentioned in the comments, you can use help(fun) to enter the help editor with the function's signature and docstring. You can also simply use print fun.__doc__ and for most mature libraries you should get reasonable documentation about the parameters and the function signature.
If you're talking about interactive help, consider using IPython which has some useful extras. For instance you could type %psource fun to get a printout of the source code for the function fun, and with tab completion you could just type wx.Frame. and then hit TAB to see a list of all of the methods and attributes available within wx.Frame.
Even though GP89 seems to have already answered this question, I thought I'd jump in with a little more detail.
First, GP89's suggestion was the use Python's built-in help() method. This is a method you can use in the interactive console. For methods, it will print the method's declaration line along with the class' docstring, if it is defined. You can also access this with <object>.__doc__ For example:
>>> def testHelp(arg1, arg2=0):
... """This is the docstring that will print when you
... call help(testHelp). testHelp.__doc__ will also
... return this string. Here is where you should
... describe your method and all its arguments."""
...
>>> help(testHelp)
Help on function testHelp in module __main__:
testHelp(arg1, arg2=0)
This is the docstring that will print when you
call help(testHelp). testHelp.__doc__ will also
return this string. Here is where you should
describe your method and all its arguments.
>>>
However, another extremely important tool for understanding methods, classes and functions is the toolkit's API. For built-in Python functions, you should check the Python Doc Library. That's where I found the documentation for the help() function. You're using wxPython, whose API can be found here, so a quick search for "wx.Frame api" and you can find this page describing all of wx.Frame's methods and variables. Unfortunately, CreatteToolBar() isn't particularly well documented but you can still see it's arguments:
CreateToolBar(self, style, winid, name)
Happy coding!

Categories