Showing posts with label introspection. Show all posts
Showing posts with label introspection. Show all posts

Wednesday, March 1, 2017

Show error numbers and codes from the os.errno module

By Vasudev Ram

While browsing the Python standard library docs, in particular the module os.errno, I got the idea of writing this small utility to display os.errno error codes and error names, which are stored in the dict os.errno.errorcode:

Here is the program, os_errno_info.py:
from __future__ import print_function
'''
os_errno_info.py
To show the error codes and 
names from the os.errno module.
Author: Vasudev Ram
Copyright 2017 Vasudev Ram
Web site: https://vasudevram.github.io
Blog: https://jugad2.blogspot.com
Product store: https://gumroad.com/vasudevram
'''

import sys
import os

def main():
    
    print("Showing error codes and names\nfrom the os.errno module:")
    print("Python sys.version:", sys.version[:6])
    print("Number of error codes:", len(os.errno.errorcode))
    print("{0:>4}{1:>8}   {2:<20}    {3:<}".format(\
        "Idx", "Code", "Name", "Message"))
    for idx, key in enumerate(sorted(os.errno.errorcode)):
        print("{0:>4}{1:>8}   {2:<20}    {3:<}".format(\
            idx, key, os.errno.errorcode[key], os.strerror(key)))

if __name__ == '__main__':
    main()
And here is the output on running it:
$ py -2 os_errno_info.py >out2 && gvim out2
Showing error codes and names
from the os.errno module:
Python sys.version: 2.7.12
Number of error codes: 86
 Idx    Code   Name                    Message
   0       1   EPERM                   Operation not permitted
   1       2   ENOENT                  No such file or directory
   2       3   ESRCH                   No such process
   3       4   EINTR                   Interrupted function call
   4       5   EIO                     Input/output error
   5       6   ENXIO                   No such device or address
   6       7   E2BIG                   Arg list too long
   7       8   ENOEXEC                 Exec format error
   8       9   EBADF                   Bad file descriptor
   9      10   ECHILD                  No child processes
  10      11   EAGAIN                  Resource temporarily unavailable
  11      12   ENOMEM                  Not enough space
  12      13   EACCES                  Permission denied
  13      14   EFAULT                  Bad address
  14      16   EBUSY                   Resource device
  15      17   EEXIST                  File exists
  16      18   EXDEV                   Improper link
  17      19   ENODEV                  No such device
  18      20   ENOTDIR                 Not a directory
  19      21   EISDIR                  Is a directory
  20      22   EINVAL                  Invalid argument
  21      23   ENFILE                  Too many open files in system
  22      24   EMFILE                  Too many open files
  23      25   ENOTTY                  Inappropriate I/O control operation
  24      27   EFBIG                   File too large
  25      28   ENOSPC                  No space left on device
  26      29   ESPIPE                  Invalid seek
  27      30   EROFS                   Read-only file system
  28      31   EMLINK                  Too many links
  29      32   EPIPE                   Broken pipe
  30      33   EDOM                    Domain error
  31      34   ERANGE                  Result too large
  32      36   EDEADLOCK               Resource deadlock avoided
  33      38   ENAMETOOLONG            Filename too long
  34      39   ENOLCK                  No locks available
  35      40   ENOSYS                  Function not implemented
  36      41   ENOTEMPTY               Directory not empty
  37      42   EILSEQ                  Illegal byte sequence
  38   10000   WSABASEERR              Unknown error
  39   10004   WSAEINTR                Unknown error
  40   10009   WSAEBADF                Unknown error
  41   10013   WSAEACCES               Unknown error
  42   10014   WSAEFAULT               Unknown error
  43   10022   WSAEINVAL               Unknown error
  44   10024   WSAEMFILE               Unknown error
  45   10035   WSAEWOULDBLOCK          Unknown error
  46   10036   WSAEINPROGRESS          Unknown error
  47   10037   WSAEALREADY             Unknown error
  48   10038   WSAENOTSOCK             Unknown error
  49   10039   WSAEDESTADDRREQ         Unknown error
  50   10040   WSAEMSGSIZE             Unknown error
  51   10041   WSAEPROTOTYPE           Unknown error
  52   10042   WSAENOPROTOOPT          Unknown error
  53   10043   WSAEPROTONOSUPPORT      Unknown error
  54   10044   WSAESOCKTNOSUPPORT      Unknown error
  55   10045   WSAEOPNOTSUPP           Unknown error
  56   10046   WSAEPFNOSUPPORT         Unknown error
  57   10047   WSAEAFNOSUPPORT         Unknown error
  58   10048   WSAEADDRINUSE           Unknown error
  59   10049   WSAEADDRNOTAVAIL        Unknown error
  60   10050   WSAENETDOWN             Unknown error
  61   10051   WSAENETUNREACH          Unknown error
  62   10052   WSAENETRESET            Unknown error
  63   10053   WSAECONNABORTED         Unknown error
  64   10054   WSAECONNRESET           Unknown error
  65   10055   WSAENOBUFS              Unknown error
  66   10056   WSAEISCONN              Unknown error
  67   10057   WSAENOTCONN             Unknown error
  68   10058   WSAESHUTDOWN            Unknown error
  69   10059   WSAETOOMANYREFS         Unknown error
  70   10060   WSAETIMEDOUT            Unknown error
  71   10061   WSAECONNREFUSED         Unknown error
  72   10062   WSAELOOP                Unknown error
  73   10063   WSAENAMETOOLONG         Unknown error
  74   10064   WSAEHOSTDOWN            Unknown error
  75   10065   WSAEHOSTUNREACH         Unknown error
  76   10066   WSAENOTEMPTY            Unknown error
  77   10067   WSAEPROCLIM             Unknown error
  78   10068   WSAEUSERS               Unknown error
  79   10069   WSAEDQUOT               Unknown error
  80   10070   WSAESTALE               Unknown error
  81   10071   WSAEREMOTE              Unknown error
  82   10091   WSASYSNOTREADY          Unknown error
  83   10092   WSAVERNOTSUPPORTED      Unknown error
  84   10093   WSANOTINITIALISED       Unknown error
  85   10101   WSAEDISCON              Unknown error

In the above Python command line, you can of course skip the "&& gvim out2" part. It is just there to automatically open the output file in gVim (text editor) after the utility runs.

The above output was from running it with Python 2.
The utility is written to also work with Python 3.
To change the command line to use Python 3, just change 2 to 3 everywhere in the above Python command :)
(You need to install or already have py, the Python Launcher for Windows, for the py command to work. If you don't have it, or are not on Windows, use python instead of py -2 or py -3 in the above python command line - after having set your OS PATH to point to Python 2 or Python 3 as wanted.)

The only differences in the output are the version message (2.x vs 3.x), and the number of error codes - 86 in Python 2 vs. 101 in Python 3.
Unix people will recognize many of the messages (EACCES, ENOENT, EBADF, etc.) as being familiar ones that you get while programming on Unix.
The error names starting with W are probably Windows-specific errors. Not sure how to get the messages for those, need to look it up. (It currently shows "Unknown error" for them.)

This above Python utility was inspired by an earlier auxiliary utility I wrote, called showsyserr.c, as part of my IBM developerWorks article, Developing a Linux command-line utility (not the main utility described in the article). Following (recursively) the link in the previous sentence will lead you to the code for both the auxiliary and the main utility, as well as the PDF version of the article.

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Managed WordPress Hosting by FlyWheel



Monday, January 30, 2017

Finding the arity of a Python function

By Vasudev Ram

While browsing through some Python code, I had the idea of trying to find the arity of a Python function. The arity of a function is the number of arguments it takes.

Digression: I had first come across the concept of arity when reading some books on functional or logic programming, years ago. E.g. Prolog has the concept of arity, IIRC. Just did a quick search for the phrase prolog arity and found a few results confirming this. Here is one, from cse.unsw.edu.au:

arity (link 1)

But in Prolog, the word arity is related to a functor (link 2), not a function, according to the page at link 2.

Excerpt from link 1:

[ The arity of a functor is the number of arguments it takes. For example, the arity of likes/2, as in likes(jane, pizza), is 2, as it takes two arguments, jane and pizza. ]

In fact the use of likes/2 gives a clue to the fact that the Erlang language was influenced by Prolog (and I've read that the first version of Erlang was written in Prolog), because a function foo with arity 2 in Erlang is written as foo/2, as for the Prolog functor above.

[ Note: I'm not an expert in either Prolog or Erlang; these are just casual observations. Anyone who knows better, feel free to comment / correct. ]

Anyway, end of digression.

Here is the code I came up with to find the arity of a Python function. I've used the isfunction and getargspec functions of the inspect module.
'''
function_arity.py
Purpose: To find the arity of a Python function.
Author: Vasudev Ram
Copyright 2017 Vasudev Ram
Web site: https://vasudevram.github.io
Blog: https://jugad2.blogspot.com
Product store: https://gumroad.com/vasudevram
'''

import inspect

# Define a few functions with increasing arity:

def f0():
    pass

def f1(a1):
    pass

def f2(a1, a2):
    pass

def f3(a1, a2, a3):
    pass

def f4(a1, a2, a3, a4):
    pass

def main():

    # Define a few non-function objects:
    int1 = 0
    float1 = 0.0 
    str1 = ''
    tup1 = ()
    lis1 = []

    # Test the function arity-finding code with both the functions 
    # and the non-function objects:
    for o in (f0, f1, f2, f3, f4, int1, float1, str1, tup1, lis1):
        if not inspect.isfunction(o):
            print repr(o), 'is not a function'
            continue
        n_args = len(inspect.getargspec(o)[0])
        if n_args == 0:
            num_suffix = '(no) args'
        elif n_args == 1:
            num_suffix = 'arg'
        else:
            num_suffix = 'args'
        print o.__name__, 'is a function that takes', \
            n_args, num_suffix
    
if __name__ == '__main__':
    main()
And here is the output when I run the program:
$ python function_arity.py
f0 is a function that takes 0 (no) args
f1 is a function that takes 1 arg
f2 is a function that takes 2 args
f3 is a function that takes 3 args
f4 is a function that takes 4 args
0 is not a function
0.0 is not a function
'' is not a function
() is not a function
[] is not a function

I also did a web search:

https://www.google.com/search?q=find+the+arity+of+python+function

in which one hit showed another way of doing it (using gettattr). This StackOverflow question:

How to find out the arity of a method in Python

had some answers; the ones by Python guru Alex Martelli (the martellibot) and Anurag Uniyal were interesting.

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Managed WordPress Hosting by FlyWheel



Wednesday, January 11, 2017

Two simple Python object introspection functions

By Vasudev Ram



While browsing some Python code and docs, I recently got the idea for, and wrote, these two simple convenience functions for introspecting Python objects.

The function oa (for object attributes) can be used to get the attributes of any Python object:
def oa(o):
    for at in dir(o):
        print at,
(The reason why I don't just type dir(o) instead of using oa(o) (for some object o), is because in IPython (though not in vanilla Python), doing just dir(o) displays the attributes in a vertical line, so the output scrolls off the screen if there are many attributes, while the oa() function prints them horizontally, so the output fits in a few lines without scrolling off.)

And running oa() a few times in the Python shell, gives (shell prompts removed):
oa({})
__class__ __cmp__ __contains__ __delattr__ __delitem__ __doc__ __eq__ __format__
__ge__ __getattribute__ __getitem__ __gt__ __hash__ __init__ __iter__ __le__ __len__ 
__lt__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __setitem__ 
__sizeof__ __str__ __subclasshook__ clear copy fromkeys get has_key items
iteritems iterkeys itervalues keys pop popitem setdefault update values viewitems 
viewkeys viewvalues

# object attributes of a list:
oa([])
__add__ __class__ __contains__ __delattr__ __delitem__ __delslice__ __doc__ __eq__ 
__format__ __ge__ __getattribute__ __getitem__ __getslice__ __gt__ __hash__ __iadd__ 
__imul__ __init__ __iter__ __le__ __len__ __lt__ __mul__ __ne__ __new__
__reduce__ __reduce_ex__ __repr__ __reversed__ __rmul__ __setattr__ __setitem__
__setslice__ __sizeof__ __str__ __subclasshook__ append count extend index insert 
pop remove reverse sort

# object attributes of an int:
oa(1)
__abs__ __add__ __and__ __class__ __cmp__ __coerce__ __delattr__ __div__ __divmod__ 
__doc__ __float__ __floordiv__ __format__ __getattribute__ __getnewargs__ __hash__ 
__hex__ __index__ __init__ __int__ __invert__ __long__ __lshift__ __mod__
__mul__ __neg__ __new__ __nonzero__ __oct__ __or__ __pos__ __pow__ __radd__ __rand__ 
__rdiv__ __rdivmod__ __reduce__ __reduce_ex__ __repr__ __rfloordiv__ __rlshift__ 
__rmod__ __rmul__ __ror__ __rpow__ __rrshift__ __rshift__ __rsub__ __rtruediv__ 
__rxor__ __setattr__ __sizeof__ __str__ __sub__ __subclasshook__ __truediv__ 
__trunc__ __xor__ bit_length conjugate denominator imag numerator real

The function oar (for object attributes regular, meaning exclude the special or "dunder" methods, i.e. those starting and ending with a double underscore) can be used to get only the "regular" attributes of any python object.
def oar(o):
    for at in dir(o):
        if not at.startswith('__') and not at.endswith('__'):
            print at,
The output from running it:
# regular object attributes of a dict:
oar({})
clear copy fromkeys get has_key items iteritems iterkeys itervalues keys pop popitem 
setdefault update values viewitems viewkeys viewvalues

# regular object attributes of an int:
oar(1)
bit_length conjugate denominator imag numerator real

# regular object attributes of a string:
oar('')
_formatter_field_name_split _formatter_parser capitalize center count decode encode 
endswith expandtabs find format index isalnum isalpha isdigit islower isspace 
istitle isupper join ljust lower lstrip partition replace rfind rindex rjust rpartition 
rsplit rstrip split splitlines startswith strip swapcase title translate upper zfill

Here are some more posts about Python introspection.

Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Managed WordPress Hosting by FlyWheel



Wednesday, October 5, 2016

Get names and types of a Python module's attributes


By Vasudev Ram



Hi readers,

Today I thought of this simple Python utility while using introspection to look at some modules.

It looks at a module, and for each attribute in it, it tells you the name and type of the attribute. This is useful if you are exploring some new Python module (built-in or third-party), and you want, for example, to know all the functions or methods in it, so that you can further introspect those by printing their docstrings, using the form:
print(module_name.function_or_method_name.__doc__)
because the docstring of a Python function of method, if present, is a nice capsule summary of: its arguments, what it does, and its return value (i.e. its input, processing and output). So with such a docstring, in many cases, a reasonably experienced programmer may not even need to look up the actual Python docs for that function or method, before beginning to use it, thereby saving their time.

So here is the utility:
from __future__ import print_function

# mod_attrs_and_types.py 
# Purpose: To show the attribute names and types 
# of a Python module, to help with learning about it.
# Author: Vasudev Ram
# Copyright 2016 Vasudev Ram
# Web site: https://vasudevram.github.io
# Blog: http://jugad2.blogspot.com
# Product store: https://gumroad.com/vasudevram

import sys

def attrs_and_types(mod_name):

    print('Attributes and their types for module {}:'.format(mod_name))
    print()
    for num, attr in enumerate(dir(eval(mod_name))):
        print("{idx}: {nam:30}  {typ}".format(
            idx=str(num + 1).rjust(4),
            nam=(mod_name + '.' + attr).ljust(30), 
            typ=type(eval(mod_name + '.' + attr))))

attrs_and_types(sys.__name__)
Running it like this:
$ python mod_attrs_and_types.py > out
gave this output:
Attributes and their types for module sys:

   1: sys.__displayhook__             <type 'builtin_function_or_method'>
   2: sys.__doc__                     <type 'str'>
   3: sys.__egginsert                 <type 'int'>
   4: sys.__excepthook__              <type 'builtin_function_or_method'>
   5: sys.__name__                    <type 'str'>
   6: sys.__package__                 <type 'NoneType'>
   7: sys.__plen                      <type 'int'>
   8: sys.__stderr__                  <type 'file'>
   9: sys.__stdin__                   <type 'file'>
  10: sys.__stdout__                  <type 'file'>
  11: sys._clear_type_cache           <type 'builtin_function_or_method'>
  12: sys._current_frames             <type 'builtin_function_or_method'>
  13: sys._getframe                   <type 'builtin_function_or_method'>
  14: sys._mercurial                  <type 'tuple'>
  15: sys.api_version                 <type 'int'>
  16: sys.argv                        <type 'list'>
  17: sys.builtin_module_names        <type 'tuple'>
  18: sys.byteorder                   <type 'str'>
  19: sys.call_tracing                <type 'builtin_function_or_method'>
  20: sys.callstats                   <type 'builtin_function_or_method'>
  21: sys.copyright                   <type 'str'>
  22: sys.displayhook                 <type 'builtin_function_or_method'>
  23: sys.dllhandle                   <type 'int'>
  24: sys.dont_write_bytecode         <type 'bool'>
  25: sys.exc_clear                   <type 'builtin_function_or_method'>
  26: sys.exc_info                    <type 'builtin_function_or_method'>
  27: sys.exc_type                    <type 'NoneType'>
  28: sys.excepthook                  <type 'builtin_function_or_method'>
  29: sys.exec_prefix                 <type 'str'>
  30: sys.executable                  <type 'str'>
  31: sys.exit                        <type 'builtin_function_or_method'>
  32: sys.flags                       <type 'sys.flags'>
  33: sys.float_info                  <type 'sys.float_info'>
  34: sys.float_repr_style            <type 'str'>
  35: sys.getcheckinterval            <type 'builtin_function_or_method'>
  36: sys.getdefaultencoding          <type 'builtin_function_or_method'>
  37: sys.getfilesystemencoding       <type 'builtin_function_or_method'>
  38: sys.getprofile                  <type 'builtin_function_or_method'>
  39: sys.getrecursionlimit           <type 'builtin_function_or_method'>
  40: sys.getrefcount                 <type 'builtin_function_or_method'>
  41: sys.getsizeof                   <type 'builtin_function_or_method'>
  42: sys.gettrace                    <type 'builtin_function_or_method'>
  43: sys.getwindowsversion           <type 'builtin_function_or_method'>
  44: sys.hexversion                  <type 'int'>
  45: sys.long_info                   <type 'sys.long_info'>
  46: sys.maxint                      <type 'int'>
  47: sys.maxsize                     <type 'int'>
  48: sys.maxunicode                  <type 'int'>
  49: sys.meta_path                   <type 'list'>
  50: sys.modules                     <type 'dict'>
  51: sys.path                        <type 'list'>
  52: sys.path_hooks                  <type 'list'>
  53: sys.path_importer_cache         <type 'dict'>
  54: sys.platform                    <type 'str'>
  55: sys.prefix                      <type 'str'>
  56: sys.py3kwarning                 <type 'bool'>
  57: sys.setcheckinterval            <type 'builtin_function_or_method'>
  58: sys.setprofile                  <type 'builtin_function_or_method'>
  59: sys.setrecursionlimit           <type 'builtin_function_or_method'>
  60: sys.settrace                    <type 'builtin_function_or_method'>
  61: sys.stderr                      <type 'file'>
  62: sys.stdin                       <type 'file'>
  63: sys.stdout                      <type 'file'>
  64: sys.subversion                  <type 'tuple'>
  65: sys.version                     <type 'str'>
  66: sys.version_info                <type 'sys.version_info'>
  67: sys.warnoptions                 <type 'list'>
  68: sys.winver                      <type 'str'>
There are other ways to do this, such as using the inspect module, but this is an easy way without inspect.

You can (e)grep for the pattern 'function|method' in the output, to get only the lines you want:

(If you haven't earlier, also check min_fgrep: minimal fgrep command in D.)
$ grep -E "function|method" out
1: sys.__displayhook__             <type 'builtin_function_or_method'>
   4: sys.__excepthook__              <type 'builtin_function_or_method'>
  11: sys._clear_type_cache           <type 'builtin_function_or_method'>
  12: sys._current_frames             <type 'builtin_function_or_method'>
  13: sys._getframe                   <type 'builtin_function_or_method'>
  19: sys.call_tracing                <type 'builtin_function_or_method'>
  20: sys.callstats                   <type 'builtin_function_or_method'>
  22: sys.displayhook                 <type 'builtin_function_or_method'>
  25: sys.exc_clear                   <type 'builtin_function_or_method'>
  26: sys.exc_info                    <type 'builtin_function_or_method'>
  28: sys.excepthook                  <type 'builtin_function_or_method'>
  31: sys.exit                        <type 'builtin_function_or_method'>
  35: sys.getcheckinterval            <type 'builtin_function_or_method'>
  36: sys.getdefaultencoding          <type 'builtin_function_or_method'>
  37: sys.getfilesystemencoding       <type 'builtin_function_or_method'>
  38: sys.getprofile                  <type 'builtin_function_or_method'>
  39: sys.getrecursionlimit           <type 'builtin_function_or_method'>
  40: sys.getrefcount                 <type 'builtin_function_or_method'>
  41: sys.getsizeof                   <type 'builtin_function_or_method'>
  42: sys.gettrace                    <type 'builtin_function_or_method'>
  43: sys.getwindowsversion           <type 'builtin_function_or_method'>
  57: sys.setcheckinterval            <type 'builtin_function_or_method'>
  58: sys.setprofile                  <type 'builtin_function_or_method'>
  59: sys.setrecursionlimit           <type 'builtin_function_or_method'>
  60: sys.settrace                    <type 'builtin_function_or_method'>
You can also (e)grep for a pattern or for alternative patterns:
$ grep -E "std(in|out)" out
   9: sys.__stdin__                   <type 'file'>
  10: sys.__stdout__                  <type 'file'>
  62: sys.stdin                       <type 'file'>
  63: sys.stdout                      <type 'file'>

The image at the top of the post is of a replica of a burning glass owned by Joseph Priestley, in his laboratory. If you don't remember your school physics, he is credited with having discovered oxygen.

- Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates on my software products / ebooks / courses.

Jump to posts: Python   DLang   xtopdf

Subscribe to my blog by email

My ActiveState recipes

Managed WordPress Hosting by FlyWheel



Wednesday, September 30, 2015

Find the caller and caller's caller of a Python function

By Vasudev Ram


While browsing some Python posts on the Net, I saw one that made use of the sys._getframe() function. I had seen that function before, and remembered that it returns information about currently active stack frames. So I looked up the docs for sys._getframe(), thinking that it might be possible to use it within a function, to find that function's caller. Turned out that it could. I also googled for terms like "find the name of the calling function in python", and found two relevant Stack Overflow (SO) posts:

Getting the caller function name inside another function in Python?

Python: How to get the caller's method name in the called method?

It can be done by using either inspect.stack() or sys._getframe(). (The inspect module in Python's standard library supports that (finding a function's caller via stack frames), as well as some other useful introspection techniques.)

(I had blogged a couple of times about inspect, earlier, here:

Python's inspect module is powerful

and here:

Using inspect.getargvalues to debug Python programs

)

So I tried out the SO code examples and slightly modified them to make these two small programs that print the name of the caller, and the caller's caller.

Here is the first program:
'''
File: find_callers.py
Run with: python find_callers.py
'''

import sys

def foo():
    print "I am foo, calling bar:"
    bar()

def bar():
    print "I am bar, calling baz:"
    baz()

def baz():
    print "I am baz:"
    caller = sys._getframe(1).f_code.co_name
    callers_caller = sys._getframe(2).f_code.co_name
    print "I was called from", caller
    print caller, "was called from", callers_caller

foo()
When run, this program outputs:
I am foo, calling bar:
I am bar, calling baz:
I am baz:
I was called from bar
bar was called from foo

The second program is similar to the one above, except that, just for fun and to do it differently, I defined part of the overall code expression as a string, interpolated the argument (2 or 3) into it using Python string formatting, and then eval()'ed the resulting string. Note that since there is now an additional function call (eval), I had to change the arguments to sys._getframe() from 1 and 2 to 2 and 3:
# File: find_callers_with_eval.py
# Run with: python find_callers_with_eval.py

import sys

getframe_expr = 'sys._getframe({}).f_code.co_name'

def foo():
    print "I am foo, calling bar:"
    bar()

def bar():
    print "I am bar, calling baz:"
    baz()

def baz():
    print "I am baz:"
    caller = eval(getframe_expr.format(2))
    callers_caller = eval(getframe_expr.format(3))
    print "I was called from", caller
    print caller, "was called from", callers_caller

foo()
The output of this second program was identical to the first.

Note that the Python documentation says:

[ CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python. ]

Also, use eval() with care, and only on trusted code.

- Vasudev Ram - Online Python training and programming

Dancing Bison Enterprises

Signup to hear about new products and services that I create.

Posts about Python  Posts about xtopdf

Monday, March 30, 2015

dunderdoc, a simple Python introspection utility

By Vasudev Ram



While browsing Python documentation, I came up with the idea of this small Python introspection utility.

It prints the __doc__ attribute (the docstring) of each item in a list of names given as argument to it. So I called the function 'dunderdoc()' - because it is an informal convention in the Python community to call attributes such as __name__, that begin and end with a double underscore, dunder-name, and so on.

Here is the code for dunderdoc.py:

"""
dunderdoc.py
A Python function to print the .__doc__ attribute (i.e. the docstring) 
of each item in a list of names given as the argument.
The function is called dunderdoc because it is an informal convention 
in the Python community to call attributes such as __name__, that begin 
and end with a double underscore, dunder-name, and so on.
Author: Vasudev Ram - http://www.dancingbison.com
Copyright 2015 Vasudev Ram
"""

def dunderdoc(names):
    for name in names:
        print '-' * 72
        print name + '.__doc__:'
        print eval(name).__doc__
    print '-' * 72

# Call dunderdoc() on some basic objects:

a = 1 # an integer
b = 'abc' # a string
c = False # a boolean
d = () # a tuple
e = [] # a list
f = {} # a dict
g = set() # a set

dunderdoc(('a', 'b', 'c', 'd', 'e', 'f', 'g'))

# Call dunderdoc() on some user-defined objects:

class Foo(object):
    """
    A class that implements Foo instances.
    """

def bar(args):
    """
    A function that implements bar functionality.
    """

dunderdoc(['Foo', 'bar'])

And here is the output of running dunderdoc.py with the example calls to the dunderdoc() function:
------------------------------------------------------------------------
a.__doc__:
int(x=0) -> int or long
int(x, base=10) -> int or long

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is floating point, the conversion truncates towards zero.
If x is outside the integer range, the function returns a long instead.

If x is not a number or if base is given, then x must be a string or
Unicode object representing an integer literal in the given base.  The
literal can be preceded by '+' or '-' and be surrounded by whitespace.
The base defaults to 10.  Valid bases are 0 and 2-36.  Base 0 means to
interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
------------------------------------------------------------------------
b.__doc__:
str(object='') -> string

Return a nice string representation of the object.
If the argument is a string, the return value is the same object.
------------------------------------------------------------------------
c.__doc__:
bool(x) -> bool

Returns True when the argument x is true, False otherwise.
The builtins True and False are the only two instances of the class bool.
The class bool is a subclass of the class int, and cannot be subclassed.
------------------------------------------------------------------------
d.__doc__:
tuple() -> empty tuple
tuple(iterable) -> tuple initialized from iterable's items

If the argument is a tuple, the return value is the same object.
------------------------------------------------------------------------
e.__doc__:
list() -> new empty list
list(iterable) -> new list initialized from iterable's items
------------------------------------------------------------------------
f.__doc__:
dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)
------------------------------------------------------------------------
g.__doc__:
set() -> new empty set object
set(iterable) -> new set object

Build an unordered collection of unique elements.
------------------------------------------------------------------------
------------------------------------------------------------------------
Foo.__doc__:

    A class that implements Foo instances.
    
------------------------------------------------------------------------
bar.__doc__:

    A function that implements bar functionality.
    
------------------------------------------------------------------------

The image at the top of the post is of Auguste Rodin's Le Penseur (The Thinker).

- Enjoy.

- Vasudev Ram - Online Python training and programming

Dancing Bison Enterprises

Signup to hear about new products that I create.

Posts about Python  Posts about xtopdf

Contact Page