Overview
The library allows you to get a string representation of a class at RUNTIME, without overloading __repr__, __str__
or defining your own specific get_repr() -> str (or even several for different cases).
The library provides a lightweight API using which you can in a minute add a pattern processing function
stringify(pattern: str) -> str to any class.
This tool is more intended to help developers that want to allow users specify their own data representation patterns.
Motivation
You're a developer. Let's assume that song_downloader is a library you wrote using strify. song_downloader.search_in_web() searchs for a song
on the Internet and returns an instance of Song class that allows to download the song using
Song.download_mp3()
>>> from song_downloader import search_in_web
>>> song = search_in_web(title='Loosing My Mind', artist='Falling In Reverse')
>>> print(song)
Song(Loosing My Mind, Falling In Reverse, 2018)
>>> song.download_mp3('[artist] — [title] ([year])')
The code will download the mp3 and save it as "Falling In Reverse — Loosing My Mind (2018).mp3".
You may ask: "why should I use strify if I could do that like *the code below*?"
>>> from song_downloader import search_in_web
>>> song = search_in_web(title='Loosing My Mind', artist='Falling In Reverse')
>>> print(song)
Song(Loosing My Mind, Falling In Reverse, 2018)
>>> song.download_mp3(f'{song.artist} — {song.title} ({song.year})')
You definitely can. But there is no flexibility for the end user that can't change sources; usually, he/she just is not assumed to do that, it's not a good practice.
strify's approach is to ask a user to enter whatever pattern he/she wants and supply it to
the program in any way (args, json, data base etc.)
and just use the value in the script. That's the power: it's not necessary to define a pattern in the source code.
Now we can change the example and save it as download_mp3.py:
from song_downloader import search_in_web
args = parse_args()
song = search_in_web(title=args['title'], artist=args['artist'])
song.download_mp3(args['mp3_name_pattern'])
Then run it like this:
python3 download_mp3.py --title='loosing my mind' --artist='falling in reverse' --mp3-name-pattern='[artist] — [title] ([year])'
And check what's happened:
user@computer:~/$ ls -1
...
download_mp3.py
Falling In Reverse — Loosing My Mind (2018).mp3
...
user@computer:~/$
Usage Guide
Let's continue with our Song class:
from random import randrange
class Song:
def __init__(self, title, artist):
self.title = title
self.artist = artist
def get_year(self):
year = randrange(1960, 2020)
return year
Notice: Song.get_year() is a mock. In a real code it has to connect to the net and
find all the data there using self.title and self.artist.
Diving in
First of all, you need to take a look at the terminology:
- stringification — process of building class instance representation (according to a pattern)
- pattern — a string with markers. After stringification all the markers
will be replaced with marker values (or
final_marker_value, see preprocessing function) - marker —
f'[{marker_name}]' - marker attribute — name of a class instance attribute which value is used during stringification.
- marker value: the way
strifygets marker value looks like this:
import inspect
def get_marker_value(class_instance, marker_attribute):
marker_value = getattr(class_instance, marker_attribute)
if inspect.ismethod(marker_value):
marker_value = marker_value()
return str(marker_value)
- preprocessing function:
def preprocessing_function(marker_value: str) -> str:
final_marker_value = whatever_magic_you_want(marker_value)
return final_marker_value
Way #1: use stringifiable and StringifyInfo
stringifiable gets list of StringifyInfo and adds stringify(pattern: str) -> str
method to the class it decorates.
One StringifyInfo in the list describes one marker.
Signature:
StringifyInfo(marker_name, marker_attribute=None, preprocessing_function=None)
If marker_attribute isn't passed to the constructor (i.e. is None) then
it's assumed to be the same as passed marker_name.
from strify import stringifiable, StringifyInfo
from random import randrange
# transforms 'tHis striNg' into 'This String'
def format_proper_name(string):
return ' '.join(word[0].upper() + word[1:].lower() for word in string.split(' '))
@stringifiable([
StringifyInfo('title', None, format_proper_name),
StringifyInfo('artist', 'artist', format_proper_name),
StringifyInfo('year', 'get_year'),
])
class Song:
def __init__(self, title, artist):
self.title = title
self.artist = artist
def get_year(self):
year = randrange(1960, 2020)
return year
Now, the following is possible:
>>> song = Song('loosing my mind', 'falling in reverse')
>>> song.stringify('[artist] — [title] ([year])')
Falling In Reverse — Loosing My Mind (2018)
>>> song.stringify('[title]: [artist], [year]')
Loosing My Mind: Falling In Reverse, 2018
Way #2: use stringifiable and stringify
Signature:
stringify(preprocessing_func=None, marker_name=None)
If marker_name equals None (i.e. not passed) then it's assumed
that marker_name == func.__name__
from strify import stringifiable, stringify
from random import randrange
# transforms 'tHis striNg' into 'This String'
def format_proper_name(string):
return ' '.join(word[0].upper() + word[1:].lower() for word in string.split(' '))
@stringifiable()
class Song:
def __init__(self, title, artist):
self._title = title
self._artist = artist
@stringify(format_proper_name)
def artist(self):
return self._artist
@stringify(format_proper_name)
def title(self):
return self._title
@stringify(marker_name='year')
def get_year(self):
year = randrange(1960, 2020)
return year
Notice: it would be more usual to make properties.
In this case, stringify must be the first decorator that's applied to the function.
@property
@stringify(format_proper_name)
def title(self):
return self._title
Now you can test the code we ran at the end of Way #1 and ensure that we achieved the same interface and results:
>>> song = Song('loosing my mind', 'falling in reverse')
>>> song.stringify('[artist] — [title] ([year])')
Falling In Reverse — Loosing My Mind (2018)
>>> song.stringify('[title]: [artist], [year]')
Loosing My Mind: Falling In Reverse, 2018
Metadata
Release files for strify 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| strify-1.0.2.tar.gz | 9.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| strify-1.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.8 kB
Release files / strify-1.0.2.tar.gz
| Download URL | strify-1.0.2.tar.gz |
|---|---|
| Size | 9.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79914a3d40916f8d8f2530c1254fc54b032e2c31c4c98de7fbbc6825b5cc5aa7
|
|
BLAKE2b-256 checksum How to use checksums |
f3d84c0a2876752fa8cb06fea544925efd58cab947c7834cdbdd306bd35dacd9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.1.0 requests-toolbelt/0.9.1 tqdm/4.41.1 CPython/3.6.9
|
Release files / strify-1.0.2-py3-none-any.whl
| Download URL | strify-1.0.2-py3-none-any.whl |
|---|---|
| Size | 13.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
31436d632605c7859e17435f532409dc889fb51866a69fc229aa3d367d4a8464
|
|
BLAKE2b-256 checksum How to use checksums |
09bff9a8fa864a7afaaf8e59c280d77b926e7973204cfe53ef535f0fc712096a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/3.1.1 pkginfo/1.5.0.1 requests/2.23.0 setuptools/45.1.0 requests-toolbelt/0.9.1 tqdm/4.41.1 CPython/3.6.9
|
Security for strify 1.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.