Skip to content

Commit fa6d74f

Browse files
committed
additional parameters added
1 parent c8fd6f2 commit fa6d74f

5 files changed

Lines changed: 155 additions & 31 deletions

File tree

README.md

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,64 @@ Simple usage:
3434

3535
```python
3636
foo = 123
37-
print(nameof(foo)) # Output: 'foo'
37+
nameof(foo) # Output: 'foo'
3838
```
3939

40-
It supports string interpolation, so it's easier to reference variable names when logging,
40+
### Additional Parameters
41+
42+
The `nameof` function supports two optional parameters for formatting the output:
43+
44+
- `wrap_in_chars`: Wraps the variable name with the specified string at the start and end.
45+
- `replace_with_whitespace`: Removes all occurrences of the specified character(s) from the variable name and replaces them with a whitespace. Accepts a string or a list of strings.
46+
47+
These are especially useful if you are logging to markdown format (printing messages as Markdown), to format variable names as code or to remove underscores for readability.
48+
49+
50+
```python
51+
some_param = 1
52+
53+
# Combine both: wrap in backticks and remove underscores
54+
nameof(some_param, "`", "_") # Output: '`some param`'
55+
56+
```
57+
58+
This is useful when generating Markdown documentation or error messages:
59+
60+
```python
61+
def validate(some_param):
62+
if some_param < 0:
63+
# Prints: The parameter `someparam` must be positive.
64+
print(f"The parameter {nameof(some_param, wrap_in_chars='`', replace_with_whitespace='_')} must be positive.")
65+
```
66+
67+
68+
### Multiple assignments
69+
70+
If a variable is assigned twice, only the first name is returned.
71+
72+
```python
73+
a = b = 1
74+
75+
nameof(a) # returns "a"
76+
nameof(b) # returns "b"
77+
78+
nameof(1) # raises ValueError (see next section below)
79+
```
80+
81+
### Error Handling
82+
83+
If you pass a value or an expression that is not a variable or attribute, `nameof` raises a `ValueError`:
84+
85+
```python
86+
nameof(42) # Raises ValueError
87+
nameof("foo.bar") # Raises ValueError
88+
nameof("nameof(bar)") # Raises ValueError
89+
```
90+
91+
92+
## More usage examples
93+
94+
`nameof()` supports string interpolation, so it's easier to reference variable names when logging,
4195
allowing for easier refactoring.
4296
In the example below, refactoring the name of second_param will propagate to the printed message without having to manually do it.
4397

@@ -54,8 +108,8 @@ It works for class attributes and instance variables.
54108
class Bar:
55109
attr = 99
56110
bar = Bar()
57-
print(nameof(Bar.attr)) # Output: 'attr'
58-
print(nameof(bar.attr)) # Output: 'attr'
111+
nameof(Bar.attr) # Output: 'attr'
112+
nameof(bar.attr) # Output: 'attr'
59113
```
60114

61115
It works also for nested classes.
@@ -71,28 +125,7 @@ class Outer:
71125
self.inner = Inner()
72126

73127
outer = Outer()
74-
print(nameof(outer.inner.value)) # Output: 'value'
75-
```
76-
77-
## Multiple assignments
78-
79-
If a variable is assigned twice, only the first name is returned.
80-
81-
```python
82-
a = b = 1
83-
84-
nameof(a) # returns "a"
85-
nameof(b) # returns "b"
86-
87-
nameof(1) # raises ValueError (see next section below)
128+
nameof(outer.inner.value) # Output: 'value'
88129
```
89130

90-
## Error Handling
91131

92-
If you pass a value or an expression that is not a variable or attribute, `nameof` raises a `ValueError`:
93-
94-
```python
95-
nameof(42) # Raises ValueError
96-
nameof("foo.bar") # Raises ValueError
97-
nameof("nameof(bar)") # Raises ValueError
98-
```

nameof/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .nameof import nameof

nameof/nameof.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,21 @@
33
from typing import Any
44
import os
55

6-
def nameof(var: Any):
6+
def nameof(var: Any, wrap_in_chars: str = "", replace_with_whitespace: str | list[str] = [] ) -> str:
7+
"""
8+
Returns the name of the variable or attribute passed as the first argument.
9+
10+
Args:
11+
var: The variable whose name should be returned.
12+
wrap_in_chars: Optional string to wrap the result with (added to start and end).
13+
replace_with_whitespace: Optional string or list of strings to replace with whitespace in the result.
14+
15+
Returns:
16+
str: The name of the variable, possibly wrapped and/or with specified characters replaced by whitespace.
17+
18+
Raises:
19+
ValueError: If the variable name cannot be determined.
20+
"""
721
# Try to extract the argument expression from the caller's source code
822
frame = inspect.currentframe()
923
code_context = None
@@ -21,10 +35,25 @@ def nameof(var: Any):
2135
if node.args:
2236
arg = node.args[0]
2337
if isinstance(arg, ast.Name):
24-
return arg.id
38+
result = arg.id
2539
# Support attribute access, e.g., nameof(obj.attr)
26-
if isinstance(arg, ast.Attribute):
27-
return arg.attr
40+
elif isinstance(arg, ast.Attribute):
41+
result = arg.attr
42+
else:
43+
continue
44+
45+
# Replace specified characters with whitespace
46+
if replace_with_whitespace:
47+
if isinstance(replace_with_whitespace, str):
48+
chars_list = [replace_with_whitespace]
49+
else:
50+
chars_list = replace_with_whitespace
51+
for ch in chars_list:
52+
result = result.replace(ch, " ")
53+
# Wrap in specified characters
54+
if wrap_in_chars:
55+
result = f"{wrap_in_chars}{result}{wrap_in_chars}"
56+
return result
2857

2958
except Exception as e:
3059
if os.environ.get("nameof_test") or os.environ.get("nameof_test_debug"):
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
if True:
2+
import sys, os
3+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
4+
os.environ["nameof_test"] = "True"
5+
6+
from nameof import nameof
7+
import pytest
8+
9+
def test_wrap_in_chars():
10+
my_var = 123
11+
assert nameof(my_var, wrap_in_chars='"') == '"my_var"'
12+
assert nameof(my_var, wrap_in_chars='*') == '*my_var*'
13+
assert nameof(my_var, wrap_in_chars='[]') == '[]my_var[]'
14+
assert nameof(my_var, wrap_in_chars='`') == '`my_var`'
15+
16+
def test_replace_with_whitespace_str():
17+
my_var = 123
18+
assert nameof(my_var, replace_with_whitespace='_') == 'my var'
19+
assert nameof(my_var, replace_with_whitespace='a') == 'my_v r'
20+
assert nameof(my_var, replace_with_whitespace='m') == ' y_var'
21+
22+
def test_replace_with_whitespace_list():
23+
my_var = 123
24+
assert nameof(my_var, replace_with_whitespace=['_']) == 'my var'
25+
assert nameof(my_var, replace_with_whitespace=['a', 'v']) == 'my_ r'
26+
27+
def test_wrap_and_remove():
28+
my_var = 123
29+
assert nameof(my_var, wrap_in_chars='`', replace_with_whitespace='_') == '`my var`'
30+
assert nameof(my_var, '`', '_') == '`my var`'
31+
assert nameof(my_var, wrap_in_chars='"', replace_with_whitespace='a') == '"my_v r"'
32+
33+
def test_attribute_access():
34+
class Dummy:
35+
attr_name = 1
36+
d = Dummy()
37+
assert nameof(d.attr_name, replace_with_whitespace='_') == 'attr name'
38+
assert nameof(d.attr_name, wrap_in_chars='`', replace_with_whitespace='_') == '`attr name`'
39+
assert nameof(d.attr_name, replace_with_whitespace='*') == 'attr_name'
40+
assert nameof(d.attr_name, replace_with_whitespace='a') == ' ttr_n me'
41+
assert nameof(d.attr_name, wrap_in_chars='`', replace_with_whitespace=['a', '_']) == "` ttr n me`"
42+
43+
44+
def run_all_tests():
45+
current_module = sys.modules[__name__]
46+
test_functions = [
47+
getattr(current_module, name)
48+
for name in dir(current_module)
49+
if name.startswith("test_") and callable(getattr(current_module, name))
50+
]
51+
for test_func in test_functions:
52+
test_func()
53+
54+
print("All tests passed.")
55+
56+
57+
if __name__ == "__main__":
58+
run_all_tests()

tests/nameof_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
if True:
2-
import os; os.environ["nameof_test"] = "True"
2+
import sys, os
3+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
4+
os.environ["nameof_test"] = "True"
5+
36
from nameof import nameof
47
import sys
58

0 commit comments

Comments
 (0)