You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This library lets you retrieve a function's source code at runtime. It can serve as a foundation for tools that work with [ASTs](https://en.wikipedia.org/wiki/Abstract_syntax_tree). It is a thin wrapper around [`inspect.getsource`](https://docs.python.org/3/library/inspect.html#inspect.getsource) and [`dill.source.getsource`](https://dill.readthedocs.io/en/latest/dill.html#dill.source.getsource).
20
21
21
-
This library is needed to obtain the source code of functions at runtime. It can be used, for example, as a basis for libraries that work with [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree) on the fly. In fact, it is a thin layer built around [`inspect.getsource`](https://docs.python.org/3/library/inspect.html#inspect.getsource) and [`dill.source.getsource`](https://dill.readthedocs.io/en/latest/dill.html#dill.source.getsource).
You can install [`getsources`](https://pypi.python.org/pypi/getsources)using pip:
33
+
You can install [`getsources`](https://pypi.python.org/pypi/getsources)with pip:
27
34
28
35
```bash
29
36
pip install getsources
30
37
```
38
+
You can also use [`instld`](https://github.com/pomponchik/instld) to quickly try this package and others without installing them.
31
39
32
-
You can also quickly try out this and other packages without having to install using [instld](https://github.com/pomponchik/instld).
33
40
41
+
## Get raw source
34
42
35
-
## Usage
43
+
The standard library provides the [`getsource`](https://docs.python.org/3/library/inspect.html#inspect.getsource) function that returns the source code of functions and other objects. However, this does not work with functions defined in the [`REPL`](https://docs.python.org/3/tutorial/interpreter.html#interactive-mode).
36
44
37
-
The basic function of the library is `getsource`, which works similarly to the function of the same name from the standard library:
45
+
This library provides a function with the same name and nearly the same interface, but without this limitation:
38
46
39
47
```python
48
+
# You can run this code snippet in the REPL.
40
49
from getsources import getsource
41
50
42
51
deffunction():
@@ -47,12 +56,37 @@ print(getsource(function))
47
56
#> ...
48
57
```
49
58
50
-
Unlike its counterpart from the standard library, this thing can also work:
59
+
This allows AST-based tools to work reliably in both scripts and the `REPL`. All other functions in the library are built on top of it.
60
+
61
+
> ⚠️ Please note that this library is intended solely for retrieving the source code of functions of any kind, including generators, async functions, regular functions, class methods, lambdas, and so on. It is not intended for classes, modules, or other objects. Other use cases may work, but they are not covered by the test suite.
62
+
51
63
52
-
- With lambda functions
53
-
- With functions defined inside REPL
64
+
## Get cleaned source
54
65
55
-
We also often need to trim excess indentation from a function object to make it easier to further process the resulting code. To do this, use the `getclearsource` function:
66
+
The [`getsource`](#get-raw-source) function returns a function's source code in raw form. This means that the code snippet captures some unnecessary surrounding code.
67
+
68
+
Here is an example where the standard `getsource` output includes extra leading whitespace:
69
+
70
+
```python
71
+
ifTrue:
72
+
deffunction():
73
+
...
74
+
75
+
print(getsource(function))
76
+
#> def function():
77
+
#> ...
78
+
```
79
+
80
+
> ↑ Notice the extra leading spaces.
81
+
82
+
For lambda functions, it may also return the entire surrounding expression:
83
+
84
+
```python
85
+
print(getsource(lambdax: x))
86
+
#> print(getsource(lambda x: x))
87
+
```
88
+
89
+
To address these issues, the library provides a function called `getclearsource`, which returns the function's source with unnecessary context removed:
To extract only the substring containing a lambda function, the library uses AST parsing behind the scenes. Unfortunately, this [does not allow](https://stackoverflow.com/a/55386046/14522393) it to distinguish between multiple lambda functions defined in a single line, so in this case you will get an exception:
108
+
109
+
```python
110
+
lambdas = [lambda: None, lambdax: x]
111
+
112
+
getclearsource(lambdas[0])
113
+
#> ...
114
+
#> getsources.errors.UncertaintyWithLambdasError: Several lambda functions are defined in a single line of code, can't determine which one.
115
+
```
116
+
117
+
If you absolutely must obtain at least some source code for these lambdas, use [`getsource`](#get-raw-source):
118
+
119
+
```python
120
+
try:
121
+
getclearsource(function)
122
+
except UncertaintyWithLambdasError:
123
+
getsource(function)
124
+
```
125
+
126
+
However, in general, the `getclearsource` function is recommended for retrieving the source code of functions when working with the AST.
127
+
128
+
129
+
## Generate source hashes
130
+
131
+
In some cases, you may not care about a function's exact source, but you still need to distinguish between different implementations. In this case, the `getsourcehash` function is useful. It returns a short hash string derived from the function's source code:
132
+
133
+
```python
134
+
from getsources import getsourcehash
135
+
136
+
deffunction():
137
+
...
138
+
139
+
print(getsourcehash(function))
140
+
#> 7SWJGZ
69
141
```
70
142
71
-
As you can see, the resulting source code text has no extra indentation, but in all other respects this function is completely identical to the usual `getsource`.
143
+
> ⓘ A hash string uses only characters from the [`Crockford Base32`](https://en.wikipedia.org/wiki/Base32) alphabet, which consists solely of uppercase English letters and digits; ambiguous characters are excluded, which makes the hash easier to read.
144
+
145
+
> ⓘ The `getsourcehash` function is built on top of [`getclearsource`](#get-cleaned-source) and ignores "extra" characters in the source code.
146
+
147
+
By default, the hash string length is 6 characters, but you can choose a length from 4 to 8 characters:
148
+
149
+
```python
150
+
print(getsourcehash(function, size=4))
151
+
#> WJGZ
152
+
print(getsourcehash(function, size=8))
153
+
#> XG7SWJGZ
154
+
```
155
+
156
+
By default, the full source code of a function is used, including its name and arguments. If you only want to compare function bodies, pass `only_body=True`:
157
+
158
+
```python
159
+
deffunction_1():
160
+
...
161
+
162
+
deffunction_2(a=5):
163
+
...
164
+
165
+
print(getsourcehash(function_1, only_body=True))
166
+
#> V587A6
167
+
print(getsourcehash(function_2, only_body=True))
168
+
#> V587A6
169
+
```
170
+
171
+
By default, docstrings are considered part of the function body. If you want to skip them as well, pass `skip_docstring=True`:
0 commit comments