Package: hopscotch
HopscotchWriting a decoupled application -- a "pluggable app" -- in Python is a common practice.
Looking for a modern registry that scales from simple use, up to rich dependency injection (DI)?hopscotchis a registry and DI package for Python 3.9+, written to support research into component-driven development for Python's web story.I expect a lot of skepticism.
In fact, I don't expect a lot of adoption.
Instead, I'm using this to learn and write articles.FeaturesSimple to complex. The easy stuff for a simple registry is easy, but rich, replaceable systems are in scope also.Better DX. Improve developer experience through deep embrace of static analysis and usage of symbols instead of magic names.Hierarchical. A cascade of parent registries helps model request lifecycles.Tested and documented. High test coverage and quality docs with lots of (tested) examples.-Extensible.Great with components. When used withviewdom, everything is wired up and you can just work in templates.Hopscotch takes its history fromwired, which came fromPyramid, which came fromZope.RequirementsPython 3.9+.venusian (for decorators)InstallationYou can installHopscotchviapipfromPyPI:$pipinstallhopscotchQuick ExamplesLet's look at: a hello world, same but with a decorator, replacement, and multiple choice.Here's a registry with one "kind of thing" in it:# One kind of thing@dataclassclassGreeter:"""A simple greeter."""greeting:str="Hello!"registry=Registry()registry.register(Greeter)# Latergreeter=registry.get(Greeter)# greeter.greeting == "Hello!"That's manual registration -- let's try with a decorator:@injectable()@dataclassclassGreeter:"""A simple greeter."""greeting:str="Hello!"registry=Registry()registry.scan()# Latergreeter=registry.get(Greeter)# greeter.greeting == "Hello!"You're building a pluggable app where people can replace builtins:# Some site might want to change a built-in.@injectable(kind=Greeter)@dataclassclassCustomGreeter:"""Provide a different ``Greeter`` in this site."""greeting:str="Howdy!"Sometimes you want aGreeterbut sometimes you want aFrenchGreeter-- for example, based on the row of data a request is processing:@injectable(kind=Greeter,context=FrenchCustomer)@dataclassclassFrenchGreeter:"""Provide a different ``Greeter`` in this site."""greeting:str="Bonjour!"# Much laterchild_registry=Registry(parent=parent_registry,context=french_customer)greeter2=child_registry.get(Greeter)# greeter2.greeting == "Bonjour!"Finally, have your data constructed for you in rich ways, including custom field "operators":@injectable()@dataclassclassSiteConfig:punctuation:str="!"@injectable()@dataclassclassGreeter:"""A simple greeter."""punctuation:str=get(SiteConfig,attr="punctuation")greeting:str="Hello"defgreet(self)->str:"""Provide a greeting."""returnf"{self.greeting}{self.punctuation}"The full code for these examples are in the docs, with more explanation (and many more examples.)And don't worry, dataclasses aren't required.
Some support is available for plain-old classes,NamedTuple, and even functions.ContributingContributions are very welcome.
To learn more, see thecontributor's guide.LicenseDistributed under the terms of theMIT license,Hopscotchis free and open source software.IssuesIf you encounter any problems,
pleasefile an issuealong with a detailed description.CreditsThis project was generated from@cjolowicz'sHypermodern Python Cookiecuttertemplate.