Skip to main content

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

    def download(self, file_name):
        with open(file_name, 'wb') as mp3:
            mp3.write(b'010101010101010')

Notice: Song.get_year() and Song.download() are mocks. In a real code they have to access 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)
  • markerf'[{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 stringifyable and StringifyInfo

from strify import stringifyable, 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(' '))

@stringifyable([
    StringifyInfo('title', 'title', 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

    def download(self, file_name):
        with open(file_name, 'wb') as mp3:
            mp3.write(b'010101010101010')

stringifyable 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, preprocessing_function=None)

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 stringifyable and stringify

from strify import stringifyable, 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(' '))

@stringifyable()
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()
    def get_year(self):
        year = randrange(1960, 2020)
        return year

    def download(self, file_name):
        with open(file_name, 'wb') as mp3:
            mp3.write(b'010101010101010')

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

strify-1.0.0.tar.gz (9.6 kB view hashes)

Uploaded Source

Built Distribution

strify-1.0.0-py3-none-any.whl (13.6 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page