SlideShare a Scribd company logo
© BST LABS 2022 ALL RIGHTS RESERVED
Generic Composite
in Python
Rationale behind the pycomposite
Open Source Library
Asher Sterkin
SVP Engineering, GM
Pyjamas Conf 2022
© BST LABS 2022 ALL RIGHTS RESERVED
● A 40-year industry veteran specializing in software architecture and technology
● SVP Engineering @ BST LABS, BlackSwan Technologies
● Former VP Technology @ NDS and Distinguished Engineer @ Cisco
● Initiator and chief technology lead of the Cloud AI Operating System project
● Areas of interest:
○ Serverless Cloud Architecture
○ (Strategic) Domain-Driven Design
○ Promise Theory and Semantic SpaceTimes
○ Complex Adaptive Systems
● Contacts:
○ Linkedin: https://www.linkedin.com/in/asher-sterkin-10a1063
○ Twitter: https://www.twitter.com/asterkin
○ Email: asher.sterkin@gmail.com
○ Slideshare: https://www.slideshare.net/AsherSterkin
○ Medium: https://asher-sterkin.medium.com/
Who am I?
© BST LABS 2022 ALL RIGHTS RESERVED
● Design Patterns
● The “Composite” Design Pattern
● Need for a Generic One
● Design Patterns Density
● Implementation
● More Advanced Case Study
● Things to Remember
Table of Contents
© BST LABS 2022 ALL RIGHTS RESERVED
The Wikipedia definition:
“In software engineering, a software design pattern
is a general, reusable solution to a commonly
occurring problem within a given context in
software design.”
Quality Without a Name (QWAN):
“To seek the timeless way we must first know the
quality without a name. There is a central quality
which is the root criterion of life and spirit in a man,
a town, a building, or a wilderness. This quality is
objective and precise, but it cannot be named.”
Design Patterns
A good collection of Design Patterns in Python:
https://github.com/faif/python-patterns
Fundamental, though a bit
outdated, with examples in C++
and Java
Origin of the idea of Design
Patterns
© BST LABS 2022 ALL RIGHTS RESERVED
Whenever we have a tree-like data structure,
be it a file system, cloud stack resources,
code, or anything composed from smaller
parts, the Composite Design Pattern is the
first candidate to consider.
“Composite” Design Pattern
© BST LABS 2022 ALL RIGHTS RESERVED
Composite Design Pattern (from Wikipedia)
Traditional OO Implementation
© BST LABS 2022 ALL RIGHTS RESERVED
● The Composite class has to overload all the abstract interface methods.
● The implementation of Composite methods would most likely be trivial:
○ Go over all child components and invoke the corresponding methods
○ Could be Depth-First or Breadth-First traversal.
○ In most of cases nobody cares which one.
● Outcome of the Composite operation is an aggregation of the child operations.
● Generally, not a big deal if the number of methods is small
Limitations of the Traditional OO Implementation
© BST LABS 2022 ALL RIGHTS RESERVED
I
F
C
services vendors APIs location
acquire
configure
consume
operate
My Context: Cloud Infrastructure From Python Code
service.py
service_config.py
© BST LABS 2022 ALL RIGHTS RESERVED
build_service_parameters()
build_service_template_variables()
build_service_conditions()
build_service_outputs()
build_service_resources()
build_function_permissions()
build_function_resources()
build_function_resource_properties()
build_function_environment()
Core Mechanism: Service Template Builder Composite
ResourceTemplateBuilder
CompositeResourceTemplateBuilder
*
● Every high-level feature (e.g. MySQL Database) has multiple sub-features:
○ Data on rest encryption: yes/no
○ Private network protection: yes/no
○ Interface: (e.g. db_connection vs SQLAlchemy)
○ Allocation: internal/external
○ Access: read-only/read-write
○ User authentication: yes/no
● Each sub-feature might have multiple flavours (e.g. type of encryption)
● Every feature or sub-feature requires one or more cloud resources
● Every service function has to be properly configured to get an access to eachy
feature resources
● Every service function implements a particular API, which might bring its own
resource feature(s)
Service Template
Builder Composite
API Template Builder
Composite
API Resource
Template Builder
Feature Template
Builder Composite
Feature Resource
Template Builder
© BST LABS 2022 ALL RIGHTS RESERVED
● The measurement of the amount of design that can
be represented as instances of design patterns
● When applied correctly, higher design pattern
density implies a higher maturity of the design.
● When applied incorrectly, leads to disastrous
over-engineering.
Design Pattern Density
© BST LABS 2022 ALL RIGHTS RESERVED
build_service_parameters()
build_service_template_variables()
build_service_conditions()
build_service_outputs()
build_service_resources()
build_function_permissions()
build_function_resources()
build_function_resource_properties()
build_function_environment()
<<null object>>
<<builder>>
ResourceTemplateBuilder
Putting All Patterns Together
*
<<composite>>
<<iterator>>
CompositeResourceTemplateBuilder
<<decorator>>
composite(cls)
<<factory method>>
get_builder(config)
XyzResourceTemplateBuilder
<<specification>>
XyzResourceSpec
*
For each method, define
a default implementation
that returns an empty
structure
<<decorator>>
ResourceTemplateBuilderDecorator
© BST LABS 2022 ALL RIGHTS RESERVED
Let’s Go to the Code
@composite
class CompositeResourceTemplateBuilder(ResourceTemplateBuilder):
"""
Implements a Composite Design Pattern to support combining multiple ResourceTemplateBuilders.
By default, works as a PassThroughGate for underlying builder(s).
"""
pass
class ResourceTemplateBuilderDecorator(ResourceTemplateBuilder):
"""
Base class for generic (e.g. Storage) ResourceTemplateBuilders selectively delegating specific tasks to
platform-dependent builders. By default, works as a StopGate for the underlying builder.
:param builder: reference to external platform-specific builder
"""
def __init__(self, builder: ResourceTemplateBuilder):
self._builder = builder
© BST LABS 2022 ALL RIGHTS RESERVED
composite class decorator
def composite(cls: type) -> type:
"""
Generic class decorator to create a Composite from the original class.
Notes:
1. the constructor does not make copy, so do not pass generators,
if you plan to invoke more than one operation.
2. it will return always flattened results of any operation.
:param cls: original class
:return: Composite version of original class
"""
attrs = {
n: _make_method(n, f)
for n, f in getmembers(cls, predicate=isfunction)
if not n.startswith("_")
}
attrs["__init__"] = _constructor
composite_cls = type(cls.__name__, cls.__bases__, attrs)
composite_cls.__iter__ = _make_iterator(composite_cls)
return composite_cls
© BST LABS 2022 ALL RIGHTS RESERVED
_constructor is simple
def _constructor(self, *parts) -> None:
self._parts = parts
© BST LABS 2022 ALL RIGHTS RESERVED
_make_method is where the real stuff is done
def _make_method(name: str, func: callable) -> callable:
> def _make_reduce(m: str, rt: type) -> callable: <-- Python Closure Gotcha
> def _make_foreach(m) -> callable: <-- Python Closure Gotcha
rt_ = signature(func).return_annotation
rt = get_origin(rt_) or rt_ # strip type annotation parameters like tuple[int, ...] if present
return _make_foreach(name) if rt is None else _make_reduce(name, rt)
# Top-down decomposition in action
© BST LABS 2022 ALL RIGHTS RESERVED
_make_foreach is boring
def _make_foreach(m) -> callable:
def _foreach_parts(self, *args, **kwargs) -> callable: <-- Python Closure Gotcha
# this is a member function, hence self
# self is iterable, concrete functions invoked depth first
for obj in self:
getattr(obj, m)(*args, **kwargs)
return _foreach_parts
© BST LABS 2022 ALL RIGHTS RESERVED
_make_reduce is more interesting
def _make_method(name: str, func: callable) -> callable:
def _make_reduce(m: str, rt: type) -> callable: <-- Python Closure Gotcha
init_value = rt()
combine = add if rt in (int, str, tuple) else always_merger.merge
def _reduce_parts(self, *args, **kwargs):
# this is a member function, hence self
# self is iterable, results come out flattened
return reduce(
lambda acc, obj: combine(acc, getattr(obj, m)(*args, **kwargs)),
self,
init_value
)
return _reduce_parts
© BST LABS 2022 ALL RIGHTS RESERVED
_make_iterator is the most tricky
def _make_iterator(cls):
def _iterator(self):
# Simple depth-first composite Iterator
# Recursive version did not work for some mysterious reason
# This one proved to be more reliable
# Credit: https://stackoverflow.com/questions/26145678/implementing-a-depth-first-tree-iterator-in-python
stack = deque(self._parts)
while stack:
# Pop out the first element in the stack
part = stack.popleft()
if cls == type(part): # The same composite exactly
stack.extendleft(reversed(part._parts))
elif isinstance(part, cls) or not isinstance(part, Iterable):
yield part # derived classes presumably have overloads
else: # Iterable
stack.extendleft(reversed(part))
return _iterator
© BST LABS 2022 ALL RIGHTS RESERVED
<<builder>>
K8SManifestBuilder
__call__(...)
<<builder>>
<<template method>>
K8SManifestPartBuilder
build_manifest_part(...)
_build_manifest_part_data(...)
__subclasses__()
More Advanced Use Case: K8S Manifest Builder
*
<<composite>>
<<iterator>>
K8SManifestPartCompositeBuilder
<<decorator>>
composite(cls)
<<factory method>>
get_builders()
K8SXYZManifestBuilder
Even with one function, this approach proved to lead to better modularity and
testability
© BST LABS 2022 ALL RIGHTS RESERVED
I need ‘em all
@composite
class K8SManifestPartCompositeBuilder(K8SManifestPartBuilder):
pass
def get_builders():
for m in iter_modules(__path__): # ensure all builders are registered
import_module(f'{__package__}.{m.name}')
return K8SManifestPartCompositeBuilder(
*(sb() for sb in K8SManifestPartBuilder.__subclasses__() if 'K8SManifestPartCompositeBuilder' not in sb.__name__)
)
© BST LABS 2022 ALL RIGHTS RESERVED
● In this solution I did not implement the Visitor Design Pattern. While it is not hard to
implement, I just did not have any real use case for it. If you do, drop me a line
● The current set of aggregation functions is a bit limited reflecting what I needed at the
moment. Support for a larger set might come in the future.
● This presentation used a more advanced version of code, currently at the separate
branch
Concluding Remarks
© BST LABS 2022 ALL RIGHTS RESERVED
● Design Patterns are extremely powerful tools.
● Design Patterns work best in concert (high density).
● Composite Design Pattern is suitable for implementing the on/off logic a complex
feature
● Also, for implementing a variable list of components you do not want to enumerate
explicitly
● Python metaprogramming could make miracles.
● With more power comes more responsibility.
● An unjustified chase after advanced techniques is a sure road to hell.
Things to Remember
© BST LABS 2022 ALL RIGHTS RESERVED
Questions?

More Related Content

PDF
Generic _Composite_ in Python_ PyWeb TLV Meetup 07.08.2024.pdf
PPTX
Composite presentation for engineering.pptx
PDF
Python Linters at Scale.pdf
ODP
Patterns in Python
 
PDF
Design Patterns in Modern C++
PPTX
Дмитрий Нестерук, Паттерны проектирования в XXI веке
PPTX
Robust Python.pptx
Generic _Composite_ in Python_ PyWeb TLV Meetup 07.08.2024.pdf
Composite presentation for engineering.pptx
Python Linters at Scale.pdf
Patterns in Python
 
Design Patterns in Modern C++
Дмитрий Нестерук, Паттерны проектирования в XXI веке
Robust Python.pptx

Similar to pyjamas22_ generic composite in python.pdf (20)

PDF
Python lecture 12
PPT
Composite pattern
PPTX
Introduction to Python
PPTX
Introduction to python
PDF
Practical C++ Generative Programming
PPTX
SFrame
PDF
Architecture Patterns with Python 1st Edition Harry Percival
PDF
Robust Python Write Clean And Maintainable Code 1st Edition Patrick Viafore
PDF
Instant download Architecture Patterns with Python 1st Edition Harry Percival...
PDF
Python Cookbook 1st Edition Alex Martelli
PDF
Python Cookbook 1st Edition Alex Martelli
PPTX
Composite pattern.pptx
PDF
Domain Driven Design Made Functional with Python
PDF
Design patterns in python v0.1
PDF
Instant download Python Cookbook 1st Edition Alex Martelli pdf all chapter
PPT
01 J2EE patrones
PPTX
5 Hours to 7.7 Seconds: How Database Tricks Sped up Rust Linting Over 2000X
PDF
Build your own discovery index of scholary e-resources
PPTX
Pydata talk
PDF
Advanced Python, Part 1
Python lecture 12
Composite pattern
Introduction to Python
Introduction to python
Practical C++ Generative Programming
SFrame
Architecture Patterns with Python 1st Edition Harry Percival
Robust Python Write Clean And Maintainable Code 1st Edition Patrick Viafore
Instant download Architecture Patterns with Python 1st Edition Harry Percival...
Python Cookbook 1st Edition Alex Martelli
Python Cookbook 1st Edition Alex Martelli
Composite pattern.pptx
Domain Driven Design Made Functional with Python
Design patterns in python v0.1
Instant download Python Cookbook 1st Edition Alex Martelli pdf all chapter
01 J2EE patrones
5 Hours to 7.7 Seconds: How Database Tricks Sped up Rust Linting Over 2000X
Build your own discovery index of scholary e-resources
Pydata talk
Advanced Python, Part 1
Ad

More from Asher Sterkin (19)

PDF
Dynamic Class Loader in TypeScript - Node.js-il Open Mic- Dec 23 2024.pdf
PDF
Ports and Adapters in TypeScript - NodeJS TLV MeetpUp - Nov 7 2024.pdf
PDF
Ported to Cloud with Wing_ Blue ZnZone app from _Hexagonal Architecture Expla...
PDF
Essence of Requirements Engineering: Pragmatic Insights for 2024
PDF
Cloud Infrastructure from Python Code: PyCon DE-23
PDF
PyCascades-23.pdf
PDF
PyConFR-23 Talk.pdf
PDF
If your computer is cloud what its Operating System look like?
PDF
Serverless flow programming a new perspective (py web meetup, sept 2nd, 2019...
PDF
Documenting serverless architectures could we do it better - o'reily sa con...
PDF
Developing cloud serverless components in Python: DDD Perspective
PDF
Shaping serverless architecture with domain driven design patterns - py web-il
PDF
Shaping serverless architecture with domain driven design patterns
PDF
Domain driven design: a gentle introduction
PDF
Strategy toolbox for startsups
PDF
AI as a service
PDF
Serverless ddd
PDF
Software strategy for startups
PDF
What is exactly anti fragile in dev ops - v3
Dynamic Class Loader in TypeScript - Node.js-il Open Mic- Dec 23 2024.pdf
Ports and Adapters in TypeScript - NodeJS TLV MeetpUp - Nov 7 2024.pdf
Ported to Cloud with Wing_ Blue ZnZone app from _Hexagonal Architecture Expla...
Essence of Requirements Engineering: Pragmatic Insights for 2024
Cloud Infrastructure from Python Code: PyCon DE-23
PyCascades-23.pdf
PyConFR-23 Talk.pdf
If your computer is cloud what its Operating System look like?
Serverless flow programming a new perspective (py web meetup, sept 2nd, 2019...
Documenting serverless architectures could we do it better - o'reily sa con...
Developing cloud serverless components in Python: DDD Perspective
Shaping serverless architecture with domain driven design patterns - py web-il
Shaping serverless architecture with domain driven design patterns
Domain driven design: a gentle introduction
Strategy toolbox for startsups
AI as a service
Serverless ddd
Software strategy for startups
What is exactly anti fragile in dev ops - v3
Ad

Recently uploaded (20)

PDF
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
PDF
System and Network Administration Chapter 2
PDF
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
PDF
Digital Strategies for Manufacturing Companies
PPTX
L1 - Introduction to python Backend.pptx
PDF
How to Choose the Right IT Partner for Your Business in Malaysia
PPTX
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
PDF
Softaken Excel to vCard Converter Software.pdf
PDF
Odoo Companies in India – Driving Business Transformation.pdf
PDF
Which alternative to Crystal Reports is best for small or large businesses.pdf
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Nekopoi APK 2025 free lastest update
PDF
Adobe Illustrator 28.6 Crack My Vision of Vector Design
PDF
System and Network Administraation Chapter 3
PDF
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
PPT
Introduction Database Management System for Course Database
PDF
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
PDF
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf
Addressing The Cult of Project Management Tools-Why Disconnected Work is Hold...
System and Network Administration Chapter 2
Adobe Premiere Pro 2025 (v24.5.0.057) Crack free
Digital Strategies for Manufacturing Companies
L1 - Introduction to python Backend.pptx
How to Choose the Right IT Partner for Your Business in Malaysia
Agentic AI Use Case- Contract Lifecycle Management (CLM).pptx
Softaken Excel to vCard Converter Software.pdf
Odoo Companies in India – Driving Business Transformation.pdf
Which alternative to Crystal Reports is best for small or large businesses.pdf
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Navsoft: AI-Powered Business Solutions & Custom Software Development
Nekopoi APK 2025 free lastest update
Adobe Illustrator 28.6 Crack My Vision of Vector Design
System and Network Administraation Chapter 3
T3DD25 TYPO3 Content Blocks - Deep Dive by André Kraus
Internet Downloader Manager (IDM) Crack 6.42 Build 42 Updates Latest 2025
Introduction Database Management System for Course Database
SAP S4 Hana Brochure 3 (PTS SYSTEMS AND SOLUTIONS)
Why TechBuilder is the Future of Pickup and Delivery App Development (1).pdf

pyjamas22_ generic composite in python.pdf

  • 1. © BST LABS 2022 ALL RIGHTS RESERVED Generic Composite in Python Rationale behind the pycomposite Open Source Library Asher Sterkin SVP Engineering, GM Pyjamas Conf 2022
  • 2. © BST LABS 2022 ALL RIGHTS RESERVED ● A 40-year industry veteran specializing in software architecture and technology ● SVP Engineering @ BST LABS, BlackSwan Technologies ● Former VP Technology @ NDS and Distinguished Engineer @ Cisco ● Initiator and chief technology lead of the Cloud AI Operating System project ● Areas of interest: ○ Serverless Cloud Architecture ○ (Strategic) Domain-Driven Design ○ Promise Theory and Semantic SpaceTimes ○ Complex Adaptive Systems ● Contacts: ○ Linkedin: https://www.linkedin.com/in/asher-sterkin-10a1063 ○ Twitter: https://www.twitter.com/asterkin ○ Email: asher.sterkin@gmail.com ○ Slideshare: https://www.slideshare.net/AsherSterkin ○ Medium: https://asher-sterkin.medium.com/ Who am I?
  • 3. © BST LABS 2022 ALL RIGHTS RESERVED ● Design Patterns ● The “Composite” Design Pattern ● Need for a Generic One ● Design Patterns Density ● Implementation ● More Advanced Case Study ● Things to Remember Table of Contents
  • 4. © BST LABS 2022 ALL RIGHTS RESERVED The Wikipedia definition: “In software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design.” Quality Without a Name (QWAN): “To seek the timeless way we must first know the quality without a name. There is a central quality which is the root criterion of life and spirit in a man, a town, a building, or a wilderness. This quality is objective and precise, but it cannot be named.” Design Patterns A good collection of Design Patterns in Python: https://github.com/faif/python-patterns Fundamental, though a bit outdated, with examples in C++ and Java Origin of the idea of Design Patterns
  • 5. © BST LABS 2022 ALL RIGHTS RESERVED Whenever we have a tree-like data structure, be it a file system, cloud stack resources, code, or anything composed from smaller parts, the Composite Design Pattern is the first candidate to consider. “Composite” Design Pattern
  • 6. © BST LABS 2022 ALL RIGHTS RESERVED Composite Design Pattern (from Wikipedia) Traditional OO Implementation
  • 7. © BST LABS 2022 ALL RIGHTS RESERVED ● The Composite class has to overload all the abstract interface methods. ● The implementation of Composite methods would most likely be trivial: ○ Go over all child components and invoke the corresponding methods ○ Could be Depth-First or Breadth-First traversal. ○ In most of cases nobody cares which one. ● Outcome of the Composite operation is an aggregation of the child operations. ● Generally, not a big deal if the number of methods is small Limitations of the Traditional OO Implementation
  • 8. © BST LABS 2022 ALL RIGHTS RESERVED I F C services vendors APIs location acquire configure consume operate My Context: Cloud Infrastructure From Python Code service.py service_config.py
  • 9. © BST LABS 2022 ALL RIGHTS RESERVED build_service_parameters() build_service_template_variables() build_service_conditions() build_service_outputs() build_service_resources() build_function_permissions() build_function_resources() build_function_resource_properties() build_function_environment() Core Mechanism: Service Template Builder Composite ResourceTemplateBuilder CompositeResourceTemplateBuilder * ● Every high-level feature (e.g. MySQL Database) has multiple sub-features: ○ Data on rest encryption: yes/no ○ Private network protection: yes/no ○ Interface: (e.g. db_connection vs SQLAlchemy) ○ Allocation: internal/external ○ Access: read-only/read-write ○ User authentication: yes/no ● Each sub-feature might have multiple flavours (e.g. type of encryption) ● Every feature or sub-feature requires one or more cloud resources ● Every service function has to be properly configured to get an access to eachy feature resources ● Every service function implements a particular API, which might bring its own resource feature(s) Service Template Builder Composite API Template Builder Composite API Resource Template Builder Feature Template Builder Composite Feature Resource Template Builder
  • 10. © BST LABS 2022 ALL RIGHTS RESERVED ● The measurement of the amount of design that can be represented as instances of design patterns ● When applied correctly, higher design pattern density implies a higher maturity of the design. ● When applied incorrectly, leads to disastrous over-engineering. Design Pattern Density
  • 11. © BST LABS 2022 ALL RIGHTS RESERVED build_service_parameters() build_service_template_variables() build_service_conditions() build_service_outputs() build_service_resources() build_function_permissions() build_function_resources() build_function_resource_properties() build_function_environment() <<null object>> <<builder>> ResourceTemplateBuilder Putting All Patterns Together * <<composite>> <<iterator>> CompositeResourceTemplateBuilder <<decorator>> composite(cls) <<factory method>> get_builder(config) XyzResourceTemplateBuilder <<specification>> XyzResourceSpec * For each method, define a default implementation that returns an empty structure <<decorator>> ResourceTemplateBuilderDecorator
  • 12. © BST LABS 2022 ALL RIGHTS RESERVED Let’s Go to the Code @composite class CompositeResourceTemplateBuilder(ResourceTemplateBuilder): """ Implements a Composite Design Pattern to support combining multiple ResourceTemplateBuilders. By default, works as a PassThroughGate for underlying builder(s). """ pass class ResourceTemplateBuilderDecorator(ResourceTemplateBuilder): """ Base class for generic (e.g. Storage) ResourceTemplateBuilders selectively delegating specific tasks to platform-dependent builders. By default, works as a StopGate for the underlying builder. :param builder: reference to external platform-specific builder """ def __init__(self, builder: ResourceTemplateBuilder): self._builder = builder
  • 13. © BST LABS 2022 ALL RIGHTS RESERVED composite class decorator def composite(cls: type) -> type: """ Generic class decorator to create a Composite from the original class. Notes: 1. the constructor does not make copy, so do not pass generators, if you plan to invoke more than one operation. 2. it will return always flattened results of any operation. :param cls: original class :return: Composite version of original class """ attrs = { n: _make_method(n, f) for n, f in getmembers(cls, predicate=isfunction) if not n.startswith("_") } attrs["__init__"] = _constructor composite_cls = type(cls.__name__, cls.__bases__, attrs) composite_cls.__iter__ = _make_iterator(composite_cls) return composite_cls
  • 14. © BST LABS 2022 ALL RIGHTS RESERVED _constructor is simple def _constructor(self, *parts) -> None: self._parts = parts
  • 15. © BST LABS 2022 ALL RIGHTS RESERVED _make_method is where the real stuff is done def _make_method(name: str, func: callable) -> callable: > def _make_reduce(m: str, rt: type) -> callable: <-- Python Closure Gotcha > def _make_foreach(m) -> callable: <-- Python Closure Gotcha rt_ = signature(func).return_annotation rt = get_origin(rt_) or rt_ # strip type annotation parameters like tuple[int, ...] if present return _make_foreach(name) if rt is None else _make_reduce(name, rt) # Top-down decomposition in action
  • 16. © BST LABS 2022 ALL RIGHTS RESERVED _make_foreach is boring def _make_foreach(m) -> callable: def _foreach_parts(self, *args, **kwargs) -> callable: <-- Python Closure Gotcha # this is a member function, hence self # self is iterable, concrete functions invoked depth first for obj in self: getattr(obj, m)(*args, **kwargs) return _foreach_parts
  • 17. © BST LABS 2022 ALL RIGHTS RESERVED _make_reduce is more interesting def _make_method(name: str, func: callable) -> callable: def _make_reduce(m: str, rt: type) -> callable: <-- Python Closure Gotcha init_value = rt() combine = add if rt in (int, str, tuple) else always_merger.merge def _reduce_parts(self, *args, **kwargs): # this is a member function, hence self # self is iterable, results come out flattened return reduce( lambda acc, obj: combine(acc, getattr(obj, m)(*args, **kwargs)), self, init_value ) return _reduce_parts
  • 18. © BST LABS 2022 ALL RIGHTS RESERVED _make_iterator is the most tricky def _make_iterator(cls): def _iterator(self): # Simple depth-first composite Iterator # Recursive version did not work for some mysterious reason # This one proved to be more reliable # Credit: https://stackoverflow.com/questions/26145678/implementing-a-depth-first-tree-iterator-in-python stack = deque(self._parts) while stack: # Pop out the first element in the stack part = stack.popleft() if cls == type(part): # The same composite exactly stack.extendleft(reversed(part._parts)) elif isinstance(part, cls) or not isinstance(part, Iterable): yield part # derived classes presumably have overloads else: # Iterable stack.extendleft(reversed(part)) return _iterator
  • 19. © BST LABS 2022 ALL RIGHTS RESERVED <<builder>> K8SManifestBuilder __call__(...) <<builder>> <<template method>> K8SManifestPartBuilder build_manifest_part(...) _build_manifest_part_data(...) __subclasses__() More Advanced Use Case: K8S Manifest Builder * <<composite>> <<iterator>> K8SManifestPartCompositeBuilder <<decorator>> composite(cls) <<factory method>> get_builders() K8SXYZManifestBuilder Even with one function, this approach proved to lead to better modularity and testability
  • 20. © BST LABS 2022 ALL RIGHTS RESERVED I need ‘em all @composite class K8SManifestPartCompositeBuilder(K8SManifestPartBuilder): pass def get_builders(): for m in iter_modules(__path__): # ensure all builders are registered import_module(f'{__package__}.{m.name}') return K8SManifestPartCompositeBuilder( *(sb() for sb in K8SManifestPartBuilder.__subclasses__() if 'K8SManifestPartCompositeBuilder' not in sb.__name__) )
  • 21. © BST LABS 2022 ALL RIGHTS RESERVED ● In this solution I did not implement the Visitor Design Pattern. While it is not hard to implement, I just did not have any real use case for it. If you do, drop me a line ● The current set of aggregation functions is a bit limited reflecting what I needed at the moment. Support for a larger set might come in the future. ● This presentation used a more advanced version of code, currently at the separate branch Concluding Remarks
  • 22. © BST LABS 2022 ALL RIGHTS RESERVED ● Design Patterns are extremely powerful tools. ● Design Patterns work best in concert (high density). ● Composite Design Pattern is suitable for implementing the on/off logic a complex feature ● Also, for implementing a variable list of components you do not want to enumerate explicitly ● Python metaprogramming could make miracles. ● With more power comes more responsibility. ● An unjustified chase after advanced techniques is a sure road to hell. Things to Remember
  • 23. © BST LABS 2022 ALL RIGHTS RESERVED Questions?