<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Rustic</title>
    <description>The latest articles on DEV Community by Alex Rustic (@alexrustic).</description>
    <link>https://dev.to/alexrustic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F537551%2F7681af89-1e45-45b4-ae1b-d4dc77aee072.png</url>
      <title>DEV Community: Alex Rustic</title>
      <link>https://dev.to/alexrustic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexrustic"/>
    <language>en</language>
    <item>
      <title>Jinbase – Multi-model transactional embedded database</title>
      <dc:creator>Alex Rustic</dc:creator>
      <pubDate>Sat, 30 Nov 2024 20:41:34 +0000</pubDate>
      <link>https://dev.to/alexrustic/jinbase-multi-model-transactional-embedded-database-2e10</link>
      <guid>https://dev.to/alexrustic/jinbase-multi-model-transactional-embedded-database-2e10</guid>
      <description>&lt;p&gt;Hi Dev !&lt;/p&gt;

&lt;p&gt;I'm Alex, a tech enthusiast. I'm excited to show you &lt;a href="https://github.com/pyrustic/jinbase" rel="noopener noreferrer"&gt;Jinbase&lt;/a&gt;, my multi-model transactional embedded database.&lt;/p&gt;

&lt;p&gt;Almost a year ago, I introduced &lt;a href="https://github.com/pyrustic/paradict" rel="noopener noreferrer"&gt;Paradict&lt;/a&gt;, my take on multi-format streaming serialization. Given its readability, the Paradict text format appears de facto as an interesting data format for config files. But using Paradict to manage config files would end up cluttering its programming interface and making it confusing for users who still have choices of alternative libraries (TOML, INI File, etc.) dedicated to config files. So I used Paradict as a dependency for &lt;a href="https://github.com/pyrustic/kvf" rel="noopener noreferrer"&gt;KvF&lt;/a&gt; (Key-value file format), a new project of mine that focuses on config files with sections.&lt;/p&gt;

&lt;p&gt;With its compact binary format, I thought Paradict would be an efficient dependency for a new project that would rely on I/O functions (such as Open, Read, Write, Seek, Tell and Close) to implement a minimalistic yet reliable persistence solution. But that was before I learned that "&lt;a href="http://danluu.com/file-consistency/" rel="noopener noreferrer"&gt;files are hard&lt;/a&gt;". &lt;a href="https://en.wikipedia.org/wiki/SQLite" rel="noopener noreferrer"&gt;SQLite&lt;/a&gt; with its &lt;a href="https://en.wikipedia.org/wiki/Database_transaction" rel="noopener noreferrer"&gt;transactions&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Object_storage" rel="noopener noreferrer"&gt;BLOB&lt;/a&gt; data type and &lt;a href="https://www.sqlite.org/c3ref/blob_open.html" rel="noopener noreferrer"&gt;incremental I/O for BLOBs&lt;/a&gt; seemed like the right giant to stand on for my new project.&lt;/p&gt;

&lt;p&gt;Jinbase started small as a &lt;a href="https://en.wikipedia.org/wiki/Key%E2%80%93value_database" rel="noopener noreferrer"&gt;key-value&lt;/a&gt; store and ended up as a multi-model embedded database that pushes the boundaries of what we usually do with SQLite. The first transition to the second data model (the depot) happened when I realized that the key-value store was not well suited for cases where a &lt;a href="https://en.wikipedia.org/wiki/Unique_identifier" rel="noopener noreferrer"&gt;unique identifier&lt;/a&gt; (UID) is supposed to be automatically generated for each new record, saving the user the burden of providing an identifier that could accidentally be subject to a collision and thus overwrite an existing record. After that, I implemented a search capability that accepts UID ranges for the depot store, timespans (records are automatically timestamped) for both the depot and key-value stores and &lt;a href="https://en.wikipedia.org/wiki/Glob_(programming)" rel="noopener noreferrer"&gt;GLOB&lt;/a&gt; patterns and number ranges for string and integer keys in the key-value store.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type)" rel="noopener noreferrer"&gt;queue&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)" rel="noopener noreferrer"&gt;stack&lt;/a&gt; data models emerged as solutions for use cases where records must be consumed in a specific order. A typical record would be retrieved and deleted from the database in a single transaction unit.&lt;/p&gt;

&lt;p&gt;Since SQLite is used as the storage engine, Jinbase supports the &lt;a href="https://en.wikipedia.org/wiki/Relational_model" rel="noopener noreferrer"&gt;relational model&lt;/a&gt; de facto. For convenience, all tables related to Jinbase internals are prefixed with &lt;code&gt;jinbase_&lt;/code&gt;, making Jinbase a useful tool for opening legacy SQLite files to add new data models that will safely coexist with the ad hoc relational model.&lt;/p&gt;

&lt;p&gt;All four main data models (key-value, depot, queue, stack) support Paradict-compatible data types, such as dictionaries, strings, binary data, integers, booleans, datetimes, etc. Under the hood, when the user initiates a write operation, Jinbase serializes (except for binary data), chunks, and stores the data iteratively. A record can be accessed not only in bulk, but also with two levels of partial access granularity: the byte-level and the field-level.&lt;/p&gt;

&lt;p&gt;While SQLite's incremental I/O for BLOBs is designed to target an individual BLOB column in a row, Jinbase extends this so that for each record, incremental reads cover all chunks as if they were a single unified BLOB. For dictionary records only, Jinbase automatically creates and maintains a lightweight &lt;a href="https://en.wikipedia.org/wiki/Database_index" rel="noopener noreferrer"&gt;index&lt;/a&gt; consisting of pointers to root fields, which then allows extracting from an arbitrary record the contents of a field automatically deserialized before being returned.&lt;/p&gt;

&lt;p&gt;The most obvious use cases for Jinbase are storing user preferences, persisting session data before exit, order-based processing of data streams, exposing data for other processes, upgrading legacy SQLite files with new data models and bespoke data persistence solutions.&lt;/p&gt;

&lt;p&gt;Jinbase is written in Python, is available on PyPI and you can play with the examples on the README.&lt;/p&gt;

&lt;p&gt;Let me know what you think of this project.&lt;/p&gt;

&lt;p&gt;Project Link: &lt;a href="https://github.com/pyrustic/jinbase" rel="noopener noreferrer"&gt;https://github.com/pyrustic/jinbase&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>database</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Jesth – Next-level human-readable data serialization format</title>
      <dc:creator>Alex Rustic</dc:creator>
      <pubDate>Thu, 18 May 2023 20:43:15 +0000</pubDate>
      <link>https://dev.to/alexrustic/jesth-next-level-human-readable-data-serialization-format-4ilo</link>
      <guid>https://dev.to/alexrustic/jesth-next-level-human-readable-data-serialization-format-4ilo</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kVClhCnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vkcgil60pvd809iwzfj7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kVClhCnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vkcgil60pvd809iwzfj7.png" alt="Jesthfile" width="800" height="842"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hi Dev !&lt;/p&gt;

&lt;p&gt;I'm Alex, a tech enthusiast. I'm excited to show you &lt;strong&gt;Jesth&lt;/strong&gt;, a next-level human-readable data serialization format.&lt;/p&gt;

&lt;p&gt;This project started out as a markup language for writing the &lt;a href="https://en.wikipedia.org/wiki/Docstring#Python"&gt;docstrings&lt;/a&gt; of functions that would ultimately be consumed by a documentation generator. Basically the idea was to split a docstring into sections like &lt;em&gt;Description&lt;/em&gt; and &lt;em&gt;Parameters&lt;/em&gt;. Each section would consist of a header in square brackets and a body (lines of text between two headers).&lt;/p&gt;

&lt;p&gt;Here's what a docstring for a sum function would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This function takes in two integers a and b and returns their sum.

[parameters]
- a: First integer
- b: Second integer

[return] 
Sum of a and b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;Description&lt;/em&gt; section in the example above is actually an anonymous section, i.e., a section with an empty header.&lt;/p&gt;

&lt;p&gt;Meanwhile, I was thinking of a way to automate part of my dev workflow by storing in a file commands grouped into tasks such as project creation, build, testing, release, et cetera. Similarly with the markup language for my documentation generator, I would use square brackets to define the tasks. Thus, a task would consist of a header and a body which would be a list of commands to be executed sequentially.&lt;/p&gt;

&lt;p&gt;I built this project and named it &lt;a href="https://github.com/pyrustic/backstage"&gt;Backstage&lt;/a&gt;. Here is a hypothetical backstage.tasks file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[release]
&amp;amp; test
&amp;amp; generate_doc
&amp;amp; git_stuff
&amp;amp; build
# upload to PyPI
$ twine upload --skip-existing dist/*

[git_stuff]
$ git add .
$ git commit -m {message}
$ git push origin master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example above is illustrative only and would not work. It contains 2 sections "release" and "git_stuff". Running the "release" task from the command line is equivalent to sequentially executing the commands in the "release" section.&lt;/p&gt;

&lt;p&gt;The documentation generator and the scripting language, despite the obvious similarity in their formats, did not share any parsing code. So, to stop repeating myself, I created a file format and its library named Jesth which stands for &lt;strong&gt;Just Extract Sections Then Hack&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The library acts as an incomplete &lt;strong&gt;INI file&lt;/strong&gt; parser that only hands the programmer the sections (as headers and their associated bodies which are lists of strings). No further interpretation of the data is done by the parser, allowing the programmer to unleash their creativity through useful hacks.&lt;/p&gt;

&lt;p&gt;In its latest iteration, Jesth has matured and also includes a proper and extensively tested hack to convert a compatible section into a dictionary data structure, making &lt;strong&gt;Jesth&lt;/strong&gt; my de facto preferred format for config files. I find Jesth more readable than &lt;strong&gt;TOML&lt;/strong&gt;, &lt;strong&gt;YAML&lt;/strong&gt;, and &lt;strong&gt;JSON&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here, encoding a dictionary data structure in its own section with another section containing a prompt for ChatGPT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[prompt]
I want you to act as a detective story writer. I will provide you with
two dictionary data structures representing the profiles of two people.
Your goal is to write a thrilling neo-noir story. My first request is:
"guess who the killer and victim is from the profiles, then build a story
that includes every detail of the profiles".

[profile]
# This section can be converted into a dictionary data structure
name = 'Jane Doe'
birthday = 2000-12-23Z10:17:37Z
photo_jpg = (bin)
    VGhpcyBpcyBub3QgYSBwaG90by4uLiBCdXQgdGhhbmsgeW91
    IGZvciB5b3VyIGludGVsbGVjdHVhbCBjdXJpb3NpdHkgOyk=
    ---
books = (dict)
    romance = (list)
        'Happy Place'
        'Romantic Comedy'
    sci-fi = (list)
        'Dune'
        'Neuromancer'
epitaph = (text)
    According to the law of conservation of energy,
    no a bit of you is gone;
    you are just less orderly.
    ---

[profile]
name = 'John Doe'
birthday = null
books = (list)
    'American Predator'
    'Mindhunter: Inside the FBI's Elite Serial Crime Unit'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can learn more by reading the project's &lt;a href="https://github.com/pyrustic/jesth#readme"&gt;README&lt;/a&gt; and playing with the &lt;a href="https://github.com/pyrustic/jesth-demo"&gt;demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let me know what you think of this project.&lt;/p&gt;

&lt;p&gt;Project Link: &lt;a href="https://github.com/pyrustic/jesth"&gt;https://github.com/pyrustic/jesth&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Wrap, augment, and override functions and methods</title>
      <dc:creator>Alex Rustic</dc:creator>
      <pubDate>Wed, 09 Nov 2022 12:44:54 +0000</pubDate>
      <link>https://dev.to/alexrustic/wrap-augment-and-override-functions-and-methods-3038</link>
      <guid>https://dev.to/alexrustic/wrap-augment-and-override-functions-and-methods-3038</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rGyXtUT0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8z82sz3mbwhph9yovxvz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rGyXtUT0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8z82sz3mbwhph9yovxvz.jpg" alt="Hooks" width="880" height="587"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;a href="https://commons.wikimedia.org/wiki/File:%D0%9A%D0%B0%D0%BD%D0%B0%D1%82%D0%BD%D1%8B%D0%B9_%D1%81%D1%82%D1%80%D0%BE%D0%BF%D1%8B.jpg"&gt;HardMediaGroup&lt;/a&gt;, &lt;a href="https://creativecommons.org/licenses/by-sa/3.0"&gt;CC BY-SA 3.0&lt;/a&gt;, via Wikimedia Commons






&lt;h2&gt;
  
  
  On decoration
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.python.org/"&gt;Python&lt;/a&gt; has a concept called &lt;a href="https://peps.python.org/pep-0318/"&gt;Decorator&lt;/a&gt; which is a function that takes another function and extends the behavior. In the following script, the &lt;code&gt;timeit&lt;/code&gt; decorator is used to measure the execution time of the &lt;code&gt;heavy_computation&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wraps&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;deco&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# execute and measure the target run time
&lt;/span&gt;            &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;total_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
            &lt;span class="c1"&gt;# print elapsed time
&lt;/span&gt;            &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;total_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;deco&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Done in {total:.3f} seconds !"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# doing some heavy computation !
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nb"&gt;test
&lt;/span&gt;Done &lt;span class="k"&gt;in &lt;/span&gt;2.001 seconds &lt;span class="o"&gt;!&lt;/span&gt;
Result: 54

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides benchmarking, there are many other cool things that can be done with the Python decorator. For example, the &lt;a href="https://hackersandslackers.com/flask-routes/"&gt;Flask&lt;/a&gt; and &lt;a href="http://bottlepy.org/docs/dev/"&gt;Bottle&lt;/a&gt; web frameworks implement routing with decorators.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hooking
&lt;/h2&gt;

&lt;p&gt;While decorators are cool, it's worth mentioning that using a decorator is much more intuitive than writing its code. The code is entirely different depending on whether the decorator takes arguments or not.&lt;/p&gt;

&lt;p&gt;The following code performs the same task as the previous one, except it is more clear and intuitive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;hooking&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;on_enter&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# execute and measure the target run time
&lt;/span&gt;    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
    &lt;span class="c1"&gt;# print elapsed time
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# get 'text' from config data
&lt;/span&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;total_time&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;on_enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Done in {total:.3f} seconds !"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# doing some heavy computation !
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nb"&gt;test
&lt;/span&gt;Done &lt;span class="k"&gt;in &lt;/span&gt;2.001 seconds &lt;span class="o"&gt;!&lt;/span&gt;
Result: 54

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Hooking library used in the code above uses Python decorators to wrap, augment, and override functions and methods. It is a generic &lt;a href="https://en.wikipedia.org/wiki/Hooking"&gt;hooking&lt;/a&gt; mechanism which is perfect for creating a &lt;strong&gt;plug-in&lt;/strong&gt; mechanism for a project, performing &lt;strong&gt;benchmarking&lt;/strong&gt; and &lt;strong&gt;debugging&lt;/strong&gt;, implementing &lt;strong&gt;routing&lt;/strong&gt; in a web framework, et cetera.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dual paradigm
&lt;/h2&gt;

&lt;p&gt;Also, it is a dual paradigm hooking mechanism since it supports &lt;strong&gt;tight&lt;/strong&gt; and &lt;strong&gt;loose&lt;/strong&gt; &lt;a href="https://en.wikipedia.org/wiki/Coupling_(computer_programming)"&gt;coupling&lt;/a&gt;. The previous code uses the tight coupling paradigm, that's why the &lt;code&gt;timeit&lt;/code&gt; hook is directly tied to the target function.&lt;/p&gt;

&lt;p&gt;In loose coupling paradigm, targets functions and methods are tagged using a decorator, and hooks are bound to these tags. So when a target is called, the bound hooks are executed upstream or downstream. This paradigm is served by a class designed for pragmatic access via &lt;a href="https://stackoverflow.com/a/38276"&gt;class methods&lt;/a&gt;. This class can be easily subclassed to group tags by theme for example.&lt;/p&gt;

&lt;p&gt;Here is an example of the loose coupling paradigm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;hooking&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;H&lt;/span&gt;

&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"heavy computation..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# doing some heavy computation !
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upstream_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"upstream hook..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downstream_hook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"downstream hook..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# bind upstream_hook and downstream_hook to the "heavy_computation" tag
&lt;/span&gt;&lt;span class="n"&gt;H&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"heavy_computation"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;upstream_hook&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;downstream_hook&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"__main__"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Result:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nb"&gt;test
&lt;/span&gt;upstream hook...
heavy computation...
downstream hook...
Result: 54

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This library is available on &lt;a href="https://github.com/pyrustic/hooking#installation"&gt;PyPI&lt;/a&gt; and you can play with the &lt;a href="https://github.com/pyrustic/hooking#examples"&gt;examples&lt;/a&gt; which are on the project's &lt;a href="https://github.com/pyrustic/hooking#readme"&gt;README&lt;/a&gt;. I would like to know what you &lt;a href="http://sl4.org/crocker.html"&gt;think&lt;/a&gt; of this project. Your questions, suggestions and criticisms are welcome !&lt;/p&gt;

&lt;p&gt;Link to the project: &lt;a href="https://github.com/pyrustic/hooking"&gt;https://github.com/pyrustic/hooking&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>productivity</category>
      <category>tutorial</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
