The Power Of The Plugin Architecture In Python

111,605
27
Published 2021-09-17
The first 1,000 people to use this link will get a 1 month free trial of Skillshare: skl.sh/arjancodes09211

By using a plugin architecture, you can create applications that are incredibly easy to extend. Add new characters or levels to your game after it's been shipped, or allow others to extend the GUI of your application dynamically, without changing a single line in the original code! In this video, I show you how to setup an architecture like this in Python in a few simple steps.

The code I worked on in this video is available here: github.com/ArjanCodes/2021-plugin-architecture.

💡 Here's my FREE 7-step guide to help you consistently design great software: arjancodes.com/designguide.

🎓 Courses:
The Software Designer Mindset: www.arjancodes.com/mindset
The Software Designer Mindset Team Packages: www.arjancodes.com/sas
The Software Architect Mindset: Pre-register now! www.arjancodes.com/architect
Next Level Python: Become a Python Expert: www.arjancodes.com/next-level-python
The 30-Day Design Challenge: www.arjancodes.com/30ddc

🛒 GEAR & RECOMMENDED BOOKS: kit.co/arjancodes.

🚀If you want to take a quantum leap in your software development career, check out my course The Software Design Mindset: www.arjancodes.com/mindset.

My software development setup:
💻 Apple M1 Macbook Air: amzn.to/3fq9nG5
⌨ Keychron K2 mechanical keyboard (awesome typing experience): amzn.to/3f1dlEI
🖱 Logitech MX Master 3: amzn.to/3sRvDyJ

My camera gear:
📷 Sony A7C - amzn.to/3aShCtw
🎙 Rode Wireless GO - amzn.to/3pSLFVU
🎙 Electrovoice RE20 - amzn.to/3xQAJ0w
🎙 Sennheiser MKH416 P48 - amzn.to/3vOpx2i
🎚 Sound Devices Mix-Pre 3 II - amzn.to/3nSOc37
💡 GVM RGB LED light - amzn.to/3qRNJid
💡 GVM 100W light with lantern softbox - amzn.to/2NuU8lk

👍 If you enjoyed this content, give this video a like. If you want to watch more of my upcoming videos, consider subscribing to my channel!

💬 Join my Discord server here: discord.arjan.codes/

🐦Twitter: twitter.com/arjancodes
🌍LinkedIn: www.linkedin.com/company/arjancodes
🕵Facebook: www.facebook.com/arjancodes

👀 Channel code reviewer board:
- Yoriz
- Ryan Laursen
- Sybren A. Stüvel

🔖 Chapters:
0:00 Intro
1:13 Explaining the example
3:22 What is the plugin architecture?
4:01 Create a factory to dynamically define game characters
9:49 Register game character classes in the factory
12:17 Dynamically insert extra code via a plugin interface
18:14 Creating a new plugin: Bard
21:56 Adding new functionality to the Bard class
23:23 Final thoughts



#arjancodes #softwaredesign #python

- Thumbnail background by Kelly Sikkema (unsplash.com/@kellysikkema) on Unsplash.


DISCLAIMER - The links in this description might be affiliate links. If you purchase a product or service through one of those links, I may receive a small commission. There is no additional charge to you. Thanks for supporting my channel so I can continue to provide you with fre

All Comments (21)
  • You can make it even more flexible by passing the factory to the 'initialize' method in the plugin from the loader. That way you don't have to import the factory. If the factory changes, you don't have to touch the plugin code at all.
  • This is an application of the Open/Closed Principle of SOLID. Quoting Uncle Bob: “Open for extension. : This means that the behavior of the module can be extended. Closed for modification.: Extending the behavior of a module does not result in changes to the source or binary code of the module.”
  • For complex / long typing hints, you can make custom typing definitions and use them.
  • @deez_gainz
    Awesome! So far you have been choosing examples that fit well to the object oriented programming paradigm, where you got clear objects like Characters or Customers or VideoExporters. Im wondering if you can discuss and come with a counter example where you would rather not use classes but just functions separated to different files / namespaces. Thanks for great work Arjan!
  • Many classes out there but very few of them touch the architecture, and very few of those explain it that great. Great job! Thanks for the euphoria per video!
  • @AlphaWatt
    You are the man Arjan! As an intermediate hobbyist developer, your series are all super super helpful. This plugin architecture layout is beautiful.
  • @JaxxedNesmith
    9:30 don't mix broad exception catching for "is my factory function name in the dict" with exception catching from running the factory function itself. If the factory throws a KeyError it will get misdiagnosed as a missing character type.
  • This is neat, indeed! Thank you for providing us with new tools and mental frameworks to expand our software development skills!
  • @theRECONN
    Honestly, amazing videos! I've only seen 3 videos of yours so far and I was not expecting such great, methodical explanations followed by actual clean, useful code and advice. Moreover, spoken by a person with, what looks to be, an arsenal of experience and insight. In the YouTube space, there is plenty of bad coding channels, which for me as a software developer is so frustrating. THIS channel is definitely one of the best I've stumbled upon. Keep up the interesting stuff!
  • @djl3009
    Thanks for another great video. I liked the creative use of the PluginInterface class to provide useful static type info for a dynamically loaded module (plugin).
  • @dedebenui
    thank you so much for your lessons on decoupling and composition, it helped me a lot in my projects and made refactoring/improving components of my application much easier !
  • @sagiziv927
    Seems interesting. In my code, I recently needed to do the same thing (adding variations without changing the existing code). I solved it using inheritance and reflection. I searched for all the subclasses of the base class and in every class I have the name as a static variable. Obviously the main issue is that it requires that all subclasses would be in the same module so they are loaded. I might try to use a factory like you did, which seems much cleaner because every subclasses would be in its own module with its own imports.
  • @devkhire2989
    This was good on Plugin dynamic loading and creating it. You explain it in very simple language. I was wondering whether you have any similar video on building python framework. Particularly on builds and distribution.
  • @NGrimthrie
    Hi Arjan, really appreciate the videos. A suggestion for a video topic is the motivation behind choosing good architecture and design patterns. I've been reading the Pragmatic Programmer and they start with a section about how all of this is motivated by a desire to increase the "ease to change" code. I see that you understand this and as you often reach the point where you say, "ah now it's very easy to change x without changing y" but your viewers may not explicitly understand this. Thanks again!
  • @csavino
    Awesome videos Arjan ! Nice channel !
  • @SkielCast
    Great video as always! Some suggestions: 1. To avoid the type ignore (15:40) I would make the PluginInterface a Protocol decorated with runtime_checkable. 2. To avoid using global objects, instead of defining the initialize as taking no arguments, I would pass an "App" object exopsing a public API of what can be changed. This may have been too much for this example but it seems like running something without parameters encourages people to use a lot of global vars and polution of the namespace 3. A more strict and parametrizable way of defining callables (5:25) is using ParamSpec or Protocols with __call__. Maybe instead of using plain dictionaries which are hard to type strictly we could have defined another Protocol called GameCharacterConfig and make GameCharacter have that as an example. That would allow dependency injection of configuration while still keeping everything type safe. When adding a new character, one should not only define a CustomCharacter class but also a CustomCharacterConfig that implements the GameConfig protocol. That way the type of that callable would be Callable[[GameCharacterConfig], GameCharacter]. If more strict typing is needed we could use a TypeVar but I believe it is not needed here.