Skip to main content

Lully

A small library providing some helpers for python devs.

See the tests for usage examples and ideas.

Popglobal

Populate global with cabalistic aliases and some of lully's objects.

import lully as ll
ll.popglob()  # use show=True to get a list of added values


ı('a', 'b', 'c')
i(['a', 'b', 'c'])
ï()

assert first('ab') == 'a'
assert last('ab') == 'b'
assert last('ab') == 'b'

If LULLY_ENABLE_REPL is set to 1, it will do this automatically when importing lully.

Funcmore

More tools for functions, notably lully.ẍ, that create a function out of two (or more):

import lully as ll
ll.ẍ(print, input)('$ ')  # --> print(input('$ '))
ll.ẍ(''.join, lambda v: v.split())('h e l l o')  # --> 'hello'
ll.ẍ(print, list)('hello')  # --> print(list('hello')) --> ['h', 'e', 'l', 'l', 'o']

And lully.ÿ that do the same thing, but splat the output in next function calls :

ll.ÿ(print, list)('hello')  # --> print(*list('hello')) --> h e l l o

Collections

Otom

The One-To-One-Mapping is a special kind of dict, where all values are also keys, associated to their key.

from lully import Otom

O = Otom({
    'one': 1,
    'two': 2,
    'tee': 3,
})
assert O[1] == 'one'
assert O['one'] == 1

An Otom acts like a dict. See tests for more.

Transformations

Date utils

Many functions, like time_since, time_from_now, pretty_seconds, parse_date, now_plus, add_time, parse_isoformat_with_microseconds may help dealing with (un)aware datetimes, relative times, and various date format.

Human-readable hashing

You may get a string like Pieuvre Minérale out of any python object with lully.human_code.

Confiseur

A Confiseur is here to help with configurations.

The principle is to subclass, for each configuration kind you need, the Confiseur base class and populate it with Bonbon instances, describing options.

from lully import Confiseur, Bonbon

class MyConfig(Confiseur):

    def bonbons(self) -> [Bonbon]:
        return (
            Bonbon('server options', 'max instances', default=3),
        )

    def validate(self, cfg):
        if cfg['server options']['max instances'] > 10:
            self.add_error(f"Can't handle more than ten instances. Provided: {cfg['server options']['max instances']}.")

myconfig = MyConfig('{"server options": { "max instances": 11 }}')
assert myconfig['server options']['max instances'] == 11
assert myconfig.has_error
assert len(myconfig.errors)

See tests for more.

Itermore

More itertools functions !

from lully.itermore import window, grouper, flatten, dotproduct, ncycles

See the source file for the full set of functions.

Kotlin-inspired functions

When coding in other languages, you get ideas. Here are the ones i got after a 1h course of Kotlin.

from lully import first, last, zip_with_next

assert first([1, 2]) == 1
assert last([1, 2]) == 2
assert first([2, 3], lambda x: x % 2) == 3
assert tuple(zip_with_next('abc')) == (('a', 'b'), ('c', None))

See the source file for the full set of functions.

Fief

This name should recall both its goal, which is FIltering of EFfective paramaters, and the fact it keeps functions to work within their fief.

You have this function:

def func(a, b):
    ...  # some implementation

and you have its parameter stored in a dict, with other keys that are not for that specific function:

config = {'a': 1, 'b': 'doe', 'loglevel': 'WARNING'}

Thus, you can't just do that:

func(**config)

Because of the expected:

TypeError: func() got an unexpected keyword argument 'loglevel'

One solution can be to filter that dict, but that's cumbersome and needs maintainance. And that's worse if you have a lot of functions to call that way.

Fief is a decorator that will make that for you, using inspect module.

from lully import fief

@fief
def func(a, b):
    return a + b

config = {'a': 2, 'b': 3, 'loglevel': 'WARNING'}

# and suddenly, you can provide anything in keywords argument:
assert func(**config) == 5   # no TypeError, that's magic !

random

lsample

This is a function answering to the n choose k problem using the Vitter's algorithm.

The problem is to choose randomly n element in a set of k. That's usually done with the random.sample(n, [1, 2, ...]) function. Hence the sampling part of lsample name. However, that stdlib function will load everything in memory, forcing you to provide a list, not a generator.

Vitter's solution is to collect the n elements during a single pass over the list, making possible to work on generators, as long as you have an idea of their size, hence not loading all data in memory.

from lully import lsample
print(lsample(3, [x for x in range(10)], it_size=10))

This enables you to pick 100 random tweets among the entire tweeter database without having to load it in memory. Provided you have enough time for the full browsing to be performed.

See that repo for more information, sources and benchmarks.

Logging

The generic lully.logger_from(o) will help you getting a logger, whatever o is. By default, it will be a logger using print statements written in stderr, but you can also provide a configured logger.

Release files for lully 3.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for lully 3.6.0
File Size Uploaded
lully-3.6.0.tar.gz 85.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for lully 3.6.0
File Interpreter ABI Platform
lully-3.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 167.8 kB

Release files / lully-3.6.0.tar.gz

Download URL lully-3.6.0.tar.gz
Size 85.6 kB
Tags Source
SHA-256 checksum
How to use checksums
367babacab3b76add54ad0e99c535a6d7bf361495ea6a8d7f8fb976c604a4214
BLAKE2b-256 checksum
How to use checksums
739fc373199d3230c83fa7ab282d12f721133de577fd048b2d9eecb8525dd4a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / lully-3.6.0-py3-none-any.whl

Download URL lully-3.6.0-py3-none-any.whl
Size 82.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e25cd305691940850415a3af8fd1db49de401d11ff9af44e13460349d9ed40aa
BLAKE2b-256 checksum
How to use checksums
161266c4e0f833eafd2d190f9e77e397304d07430d3eba932259150f22bede19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.6.0 This release

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.0

2 release files

3.2.0

2 release files

2.3.0

1 release file

2.2.0

1 release file

2.1.0

1 release file

2.0.0

1 release file

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page