Coder Social home page Coder Social logo

t184256 / hacks Goto Github PK

View Code? Open in Web Editor NEW
17.0 4.0 0.0 39 KB

hacks, a python plugin library that doesn't play by the rules

License: MIT License

Python 100.00%
python python3 aspect-oriented aspects hacks preposition-based library plugin-system plugins

hacks's Introduction

hacks, a Python plugin library that doesn't play by the rules

In one sentence

hacks aims to be a plugin library bringing aspect-oriented approach to Python programs.

About

What is hacks about? Oh, let me see...

hacks is about shooting a complete stranger's leg from around the corner. Stealthily. Modularly. Painfully.

hacks is about patching objects that you're allowed to patch, and then patching everything else.

hacks is about hooking into other code from afar.

hacks is about modifying and extending functions without even having a reference to them.

hacks is about extending classes you'll never see and existing instances of them that you'll never meet. As you see fit. With null sweat. And that feeling of impending doom.

And hacks is about cleaning up all that mess with a single dedent.

Usage

import hacks

# Hacks is usable as a simple call dispatcher:


def main_code():
    N = 2
    for i in range(N):
        # call everyone hooked into 'explicit_call'
        hacks.call.explicit_call(i)
# ...


@hacks.into('explicit_call')  # knows nothing about main_code
def plugin1(i):
    print(i)


class Plugin2:

    @hacks.into('explicit_call')
    @hacks.stealing  # that's a pity that they forgot to expose N
    def method(self, i, N=hacks.steal):
        print(i, 'of', N)

# ...
with hacks.use(plugin1, Plugin2):
    main_code()  # prints '0', '0 of 2', '1', '1 of 2'
main_code()  # print nothing


# Hacks can be used to modify @hacks.friendly objects, e.g. wrap functions:

@hacks.friendly  # name not specified, pass 'greeter' to @hacks.around
def greeter():
    return 'Hello!'
# ...


@hacks.around('greeter')
def reverse(func):
    def func_reversed():
        return func()[::-1]
    return func_reversed


# ...
with hacks.use(reverse):  # reverses all 'printer's
    print(greeter())  # Prints '!olleH'
print(greeter())  # Prints 'Hello!'


# There is special support for extending classes and mutating existing
# instances to use the updated versions transparently:

@hacks.friendly_class  # name not specified, pass 'Clock' to @hacks.up
class Clock:
    def __init__(self, text):
        self._text = text
    def tick(self):
        return self._text


# ...
@hacks.up('Clock')
def tockify(clock_class):
    class TockingClock:
        def tick(self):
            return self._text + '-tock'
    return TockingClock

# ...
ticker = Clock('tick')
print(ticker.tick())  # prints 'tick'

with hacks.use(tockify):  # makes all 'clock's tock
    print(ticker.tick())  # prints 'tick-tock'
    # Yes, ticker has transparently changed its class to TockingClock.

print(ticker.tick())  # prints 'tick'
# And now it changed back to a plain old Clock!

There's more: initializers, deinitializers, modifying custom objects and making non-cooperating objects cooperate.

But the most great feature is hacks.use, enabling and disabling hacks gracefully without altering their behaviour outside that block or thread. It can be nested, it can be overridden, it poisons the call stack and it cleans up after itself at the end of the block. Ain't that nice?

Please see tests directory for more powerful usage examples.

Meh, extending code with hacks is too explicit!

Ah, the object of your interest is not decorated? No problems, monkeypatch it with @hacks_friendly:

sys.stdin = hacks_friendly(sys.stdin)

That alone should have near-zero visible effect. Now change, wrap and extend it however you want with hacks.use(...) without worrying about other threads or cleaning up after yourself.

Meh, extending code with hacks is too implicit!

When you decorate something with @hacks.friendly/friendly_class, you kind of set it free from all contracts. You never know what it will be on next access. But hey, any decorator is a custom modification, what did you expect?

By the way, that burden of responsibility didn't simply vanish, it just spread onto the person who @hacks.up and around your code.

If you're opposed to stealing, simply don't steal.

And how exactly is that a plugin system?

It's usable as one.

Write some code. Decorate it with @hacks.friendly and @hacks.friendly_class paired with nice names. Add explicit calls to plugins with hacks.call where appropriate.

Think of a new aspect, like logging. Write the logging code that @hacks.into explicit plugin calls. Wrap around entire functions to add more logging. Modify classes if you want to.

Then execute your code with hacks.use(logging):. Enjoy your pluggable, replaceable, separated logging without polluting the original code too much.

Write more powerful and game-changing modifications without touching the main code. Stack, nest and apply multiple hacks... um, plugins. Define their 'scopes' flexibly. Don't break original object's contracts without a good reason.

What else do you need from a plugin system?

Setuptools integration for plugins autodiscovery, right. It is planned for future releases though. Pull requests are welcome!

So what is the plugin interface? Plugins need a rigid interface!

Not in Pythonland.

If you disagree with me, you must be an interesting and strange person that should probably have a look at https://github.com/pytest-dev/pluggy

Choosing names forces you to design an interface!

Really? How unbearable. Just hook all hacks to 'x', my true anarchist. Good luck differentiating the objects though!

Installation (approximate)

From pypi:

pip3 install hacks

From Github:

pip3 install git+https://github.com/t184256/hacks.git

From local source:

pip3 install -r requirements.txt
python3 setup.py install

More

hacks gave life and purpose to a generic object proxying library called mutants. Check it out: https://github.com/t184256/mutants

License

hacks is distributed under the terms of the MIT License; see LICENSE.txt.

hacks's People

Contributors

t184256 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

hacks's Issues

add setuptools integration

Using hacks as a plugin system with hacks.call(...) dispatcher just begs for setuptools-based plugins registration via entry_points.

Use case:

  • A class with several @hacks.into-decorated methods is ready to serve a good job as a plugin.
  • An opt-in form of with hacks.use(..., setuptools_autodiscovered=True) enables it.
  • CRASH/BURN!

Add initializers/deinitializers

Allow initializers/deinitializers for hacks.

It seems to be a standard and expected functionlaity for plugin systems.

Execute them in forward order in Registry's enter and in reverse order in exit.

Consider stripping stackframes

Possible ways:

  • flatten call tree within hacks (hurts readability)
  • use _call_with_frames_removed convention (is it applicable?)

Unclash __enter__ and __exit__

Should I rename enter and exit to hacks_enter and hacks_exit to make hack-grouping objects usable as standalone context managers?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.