Here you'll find a few implementation notes about avax-python. This is mainly targeted at developers who wish to work on the Python source code.
Golang object orientation is different from the Python OO approach. As a result, we had to make the choice of making the code more pythonic by using idioms not traditionally used in Go - or to stick with the Go approach.
We stuck with the Go approach. Here's why.
The Avalanche Go implementation is being heavily developed right now - and will be for the foreseable future. Keeping things as similar as possible with the reference implementation allows us to quickly understand new changes to the Ava Labs repo.
If we implemented the Python version using our own structure, class, method and variable names, we'd need to spend a lot of time understanding each new commit on the Go version and then translating the idea back and forth between the Python implementation.
Thus, the Python code uses snake casing,
Initialize methods and empty constructors (see next section), file names may appear quirky and kind of redundant and so on. This was done on purpose. We don't really like it either, because the Python code could be a lot more concise. But doing it this way will allow us to try and keep up with the fast paced development over at the main repo.
Paths, module and class names are especially prone to redundancy under this approach. Since Go bundles everything in a package together automatically, things declared on the global scope of one source file can be automatically seen by other files in the same package. This isn't the case in Python. So we must explicitly import stuff from the same directory, which makes the headers and file names look very redundant.
Also, Go makes
UpperCase names public and
lowerCase ones package-private. In Python, everything's public by default. Some Avalanche Go classes export a public interface using the UpperCase methods and then perform the heavy lifting in lowerCase methods. So, when the names matched exactly, except for the upper/lower case, and the lower case version was not called elsewhere, we've refactored the lower case code to the PublicVersion() of the method calls. If the lower case version gets called elsewhere, then we've left the implementation as it is in Go.
In Python we have two multitasking models: process or thread based. We've implemented an abstraction where developers can choose the MT or MP models by passing "thread" or "process" options to
avaxpython.parallel.Parallel class. Right now it's a very primitive implementation, it just switches between
For the sake of simplicity, our initial Python implementation is synchronous. Messages arrive from the network and flow through the pipeline, finding their way to the consensus engines and VM's synchronously.
We've tested some constructs which kinda mimic Go's multitasking constructs, but this part of the code is largely untested and still in development.
Thus, you'll find that, for now, we've removed locks, mutexes and tons of other synchronization code until we decide how to proceed. Since the Avalanche network is still int its infancy, we're able to easily process all messages synchronously. avax-python's multitasking constructs should eventually catch up with the Go implementation and go fully asynchronous.
In the future, Avalanche nodes will likely require more advanced event-driven architectures using Kafka clusters, for example. For now we've kept it as simple as possible.
avax-python serializable classes require an empty constructor.
(Note that throughout the code we use marshal/unmarshal to mean the same as serialize/deserialize.)
The reason for this is to make our job easier + the way the Avalanche Codecs work.
When unmarshaling an object, we create an empty instance of it by calling its
class() name like so. In an earlier implementation, it made things really complicated to dive into the object's
dir to find constructor parameters to rebuild the constructor call on the fly.
So we transferred the burden of initializing the objects from the Codec to the implementor.