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.
Project description
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
strify
gets 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file strify-1.0.2.tar.gz
.
File metadata
- Download URL: strify-1.0.2.tar.gz
- Upload date:
- Size: 9.9 kB
- Tags: Source
- Uploaded using 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
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 79914a3d40916f8d8f2530c1254fc54b032e2c31c4c98de7fbbc6825b5cc5aa7 |
|
MD5 | d50cfdc30e1ca3fab726225c2bf40e93 |
|
BLAKE2b-256 | f3d84c0a2876752fa8cb06fea544925efd58cab947c7834cdbdd306bd35dacd9 |
File details
Details for the file strify-1.0.2-py3-none-any.whl
.
File metadata
- Download URL: strify-1.0.2-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using 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
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 31436d632605c7859e17435f532409dc889fb51866a69fc229aa3d367d4a8464 |
|
MD5 | e71b772c43029ab83bbcf9ba7fa79ccc |
|
BLAKE2b-256 | 09bff9a8fa864a7afaaf8e59c280d77b926e7973204cfe53ef535f0fc712096a |