This comprehensive guide to Python mocking will help you keep your unit tests straightforward and explain how to best use the mock.unittest library to your advantage. How does the Python Static method work? When you create a class, you usually define a few methods and properties. They may or may not have access to the class instance. There three types of methods depending upon their access are Instance Method, Class Method, and Static Method. This tutorial will cover the application of Static Methods.
- Iron On Patches
- Python Static Method Call
- Python Method May Be Static
- Python Monkey Patch Static Method Examples
MakeCode programs can be authored in Blocks, Static TypeScript or Static Python.
Both Blocks and Static Python are converted to Static TypeScript before being compiled to lower-level languages.Blocks is implemented using Google Blockly.
Static TypeScript is a subset of TypeScript. Currently, we are using TypeScript version 2.6.1. TypeScript itself is a superset of JavaScript, and many MakeCode programs, especially at the beginner’s level, are also just plain JavaScript.There are more technical details about the language and the compiler in this MPLR 2019 paper.
MakeCode is meant for teaching programming first, and JavaScript second. For thisreason, we have stayed away from concepts that are specific to JavaScript (forexample, prototype inheritance), and instead focused on ones common to mostmodern programming languages (for example, loops, lexically scoped variables,functions, lambdas, classes).
Static Python is not still in an Experimental phase and might not be available in your editor.
Supported language features
- variable declarations with
let
,const
- functions with lexical scoping and recursion
- top-level code in the file; hello world really is
console.log('Hello world')
if ... else if ... else
statementswhile
anddo ... while
loopsfor(;;)
loopsfor ... of
statements (see below aboutfor ... in
)break/continue
; also with labeled loopsswitch
statement (on numbers, strings, and arbitrary types - the last one isn’t very useful)debugger
statement for breakpoints- conditional operator
? :
; lazy boolean operators - namespaces (a form of modules)
- all arithmetic operators (including bitwise operators)
- strings (with a few common methods)
- string templates (
`x is ${x}`
) - arrow functions
() => ...
- passing functions (with up to 3 arguments) as values
- classes with static and instance fields, methods and constructors;
new
keyword - array literals
[1, 2, 3]
- enumerations (
enum
) - asynchronous functions that look synchronous to the user
- method-like properties (get/set accessors)
- basic generic classes, methods, and functions
- class inheritance
- classes implementing interfaces (explicitly and implicitly)
- object literals
{ foo: 1, bar: 'two' }
typeof
expressionpublic
/private
annotations on constructor arguments (syntactic sugar to make them into fields)- initializers for class fields
- lambda functions with more than three arguments
- using generic functions as values and nested generic functions
- binding with arrays or objects:
let [a, b] = ...; let { x, y } = ...
- exceptions (
throw
,try ... catch
,try ... finally
) - downcasts of a superclass to a subclass
- function parameter bi-variance
- explicit or implicit use of the
any
type union
orintersection
types- using a generic function as a value
- class inheritance for generic classes and methods
delete
statement (on object created with{...}
)- object destructuring with initializers
- shorthand properties (
{a, b: 1}
parsed as{a: a, b: 1}
) - computed property names (
{[foo()]: 1, bar: 2}
)
Unsupported language features
Static TypeScript has nominal typing for classes, rather than the structural typing of TypeScript. In particular, it does not support:
interface
with same name as aclass
- casts of a non-
class
type to aclass
interface
that extends a aclass
- inheriting from a built-in type
this
used outside of a method- function overloading
Things you may miss and we may implement:
- spread and reset operators (statically typed)
- support of
enums
as run-time arrays new
on non-class types- using a built-in function as a value
Things that we are not very likely to implement due to the scope of the projector other constraints (note that if you don’t know what a given feature is, you’reunlikely to miss it):
- file-based modules (
import * from ...
,module.exports
etc); we do support namespaces yield
expression andfunction*
await
expression andasync function
- tagged templates
tag `text ${expression} more text`
are limited to special compiler featureslike image literals; regular templates are supported with
statementeval
for ... in
statements (for ... of
is supported)- prototype-based inheritance;
this
pointer outside classes arguments
keyword;.apply
method- JSX (HTML fragments as part of JavaScript)
Static TypeScript has somewhat stricter ideas of scoping than regular TypeScript.In particular var
is not allowed (let
and const
are supported),and identifiers defined with function
can only be used after all variablesfrom outer scopes have been defined.(The closure objects for functions that are used before definitionis constructed right after last used variable have been defined.For functions defined before usage, the closure is constructed at thepoint of definition.)Both of the following examples will yield a compile error.
For JS-only targets we may implement the following:
- regular expressions
Note, that you can use all of these while implementing your runtime environment(simulator), they just cannot be used in user’s programs.
Semantic differences against JavaScript
As such, it isn’t really feasible to run a full JavaScript virtual machinein 3k of RAM, and thus PXT programs are statically compiled to native code to run efficiently.
PXT used to support a legacy compilation strategy, where numbers were representedas 32 bit signed integers, and all types were static.This is used by the v0
branch of micro:bit (but not the current v1
) and the Chibitronics editors, but is no longer included in the main PXT code base.
PXT follows nominal typing for classes.This means that if you declare x
to be of class type C
, and at runtimeit happens to be not of this type, then when you try to access fieldsor methods of x
you will get an exception, just as if x
was null
.
It is also impossible to monkey-patchclasses by overriding methods on class instance.As prototype chains are not accessible or even used, it’s also not possible tomonkey-patch these.
Finally, classes are currently not extensible with arbitrary fields.We might lift this in future.
Object.keys(x)
is not yet supported when x
is dynamically a class type.It is supported when x
was created with an object literal (eg., {}
or { a: 1, b: 'foo' }
).The order in which properties are returned is order of insertion with nospecial regard for keys that looks like integer (JavaScript has really counter-intuitive behaviorhere).When we support Object.keys()
on class types, the order will be the static order offield definition.
Execution environments
PXT programs are executed in at least three different environments:
- microcontrollers, with native code compilation (ARM)
- browsers
- server-side JavaScript engines (node.js, etc)
We refer to the browser execution environment as the “simulator” (of themicrocontroller), even though for some targets it’s the only environment.
The node.js execution is currently only used for automated testing, but onecan easily imagine a programming experience for scripts running on headlessdevices, either locally or in the cloud.
In case of microcontrollers, PXT programs are compiled in the browserto ARM Thumb assembly, and then to machine code, resulting in a filewhich is then deployed to the microcontroller,usually via USB mass-storage interface.
For browsers and node.js, PXT programs are compiled to continuation-passing styleJavaScript. This utilizes the TypeScript abstract syntax tree as input, butdoes not use TypeScript JavaScript emitter.On the plus side, this allows for handling of async calls, even if the browserdoesn’t support yield
statement, as well as cross-browser and remotedebugging. On the other hand, the generated code is not really human readable.
Numbers are either tagged 31-bit signed integers, or if they do not fit boxed doubles. Special constants like false
, null
andundefined
are given special values and can be distinguished.We’re aiming at full JavaScript compatibility here.
Static compilation vs a dynamic VM
PXT programs are compiled to native code. The only currently supportednative target is ARM Thumb.PXT used to support two different AVR ports, but these have beenremoved together with the legacy compilation strategy.
Compared to a typical dynamic JavaScript engine, PXT compiles code statically,giving rise to significant time and space performance improvements:
- user programs are compiled directly to machine code, and arenever in any byte-code form that needs to be interpreted; this results inmuch faster execution than a typical JS interpreter
- there is no RAM overhead for user-code - all code sits in flash; in a dynamic VMthere are usually some data-structures representing code
- due to lack of boxing for small integers and static class layout the memory consumption for objectsis around half the one you get in a dynamic VM (not countingthe user-code structures mentioned above)
- while there is some runtime support code in PXT, it’s typically around 100KB smaller thana dynamic VM, bringing down flash consumption and leaving more space for user code
The execution time, RAM and flash consumption of PXT code is as a rule of thumb 2x ofcompiled C code, making it competitive to write drivers and other user-space libraries.
Iron On Patches
Interfacing C++ from PXT is easier than interfacing typical dynamic VMs,in particular for simple functions with numbers on input and output - there isno need for unboxing, checking types, or memory management.
The main disadvantage of using static compilation is lack of dynamic featuresin the language (think eval
), as explained above.
While it is possible to run a dynamic VM even on an nRF51-class device(256KB of flash, 16KB of RAM), it leaves little space for innovative featureson the software side, or more complex user programs and user-space (not C++) drivers.
Smaller int types
As noted above, when performing computations numbers are treated as doubles. However, when you store numbers in global variables or (soon) record fields youcan choose to use a smaller int type to save memory. Microcontrollers typically have very little memory left, so these few bytes saved here and there (especially in commonly used packages) do add up.
The supported types are:
uint8
with range0
to255
uint16
with range0
to65536
int8
with range-128
to127
int16
with range-32768
to32767
int32
with range-2147483648
to2147483647
uint32
with range0
to4294967295
Python Static Method Call
If you attempt to store a number exceeding the range of the small int type, onlythe lowest 8 or 16 bits will be stored. There is no clamping nor overflow exceptions.
If you just use number
type (or specify no type at all) in tagged strategy,then if the number fits in signed 31 bits, 4 bytes of memory will be used.Otherwise, the 4 bytes will point to a heap-allocated double (all together,with memory allocator overhead, around 20 bytes).
In legacy strategy, number
is equivalent to int32
, and there is no uint32
.
Limitations
- arrays of int types are currently not supported; you can use a
Buffer
instead - locals and parameters of int types are not supported
Near future work
There are following differences currently, which should be fixed soon.They are mostly missing bridges between static, nominally typed classes,and dynamic maps.
- default parameters are resolved at call site; they should be resolved in thecalled method so eg. virtual methods can have different defaults
x.foo
, wherex
is class andfoo
is method cannot be currently used as a value;we could make it equivalent to JavaScript’sx.foo.bind(x)
Object.keys()
is currently not implemented for classes; when it will bethe order of fields will be static declaration order- how to validate types of C++ classes (Pin mostly)?
Python Method May Be Static
Supported language features
The following language features should be fully supported and work according to the Python 3 language specification.
- lists
- dictionaries
- function definitions
- function calling
- method calling
- calling into any MakeCode library code
- literals: strings, numbers, boolean, None
- many list methods: pop, clear, index, count, len
- many string methods: casefold, capitalize, center, count, endswith, find, index, isalnum, isalph, isascii, isdigit, isnumeric, isspace, isdecimal, isidentifier, islower, isprintable, istitle, issupper, join, ljust, lower, lstrip, replace, rfind, rindex, rjust, rsplit, rstrip, split, splitlines, startswith, strip, swapcase, title, upper, zfill, ord
- many math functions: int, min, max, abs, randint
- while loop
- for-in loop with range(), array or string literal
- break, continue
- conditional statements (if, elif, else)
- pass statement
- variables*
- if expression / ternary operator
- comparison (in, notin)
- byte literal
- type annotations using “:” syntax
- slice notation**
- lambda
: variable semantics have slightly different scoping rules than Python 3 and global & nonlocal keywords are unsupported.*: some slice notation is not yet supported
Not supported language features
The following language features are not yet supported.
- with
- assert
- classes
- __constructor
- super()
- global & nonlocal
- raise
- try
- generators
- attributes
- import, import from
- sets
- list comprehensions
- set comprehensions
- dictionary comprehensions
- await
- yield, yield from
- format strings
- arrays
- all list, string, math methods not listed above
- *args / varargs
Edit this page on GitHub
Edit template of this page on GitHub
Monkey patching is a technique to modify module functions or class methods in Python and other dynamic languages run-time. It differs from the traditional source code patching that it does not need separate utility or compilation process to become effective. This means that you can deploy patches to codebase not under your control with your application without extra effort. Monkey patching has been made famous by Plone/Zope community where there is even collective.monkeypatcher add-on for managed monkey patching.
Because monkey patches do not need a compilation stage, the patch will work with the future versions of the application, assuming the patched function or method is not changed. So you can “safely” update the patched software and the patch will apply to the new version without need to go to command-line to perform some cumbersome commands. However, it is the best practice of open source community to report the bugs and submit the fixing patches, as source code patch, in the corresponding issue trackers.
Python Monkey Patch Static Method Examples
In Django context, you can use monkey patching to
- Fix bugs or modify features of Django core without touching the source code
- Fix bugs or modify features of Django plug-ins (TinyMCE, filebrowser, Django CMS) without touching the source code
Patches are usually applied when Python does module imports. You have a special module called “monkeypatches.py” and when that is imported, it applies the patches when the module body level code runs. However, it is difficult to find stable import point in Django to run monkey patching. Django does some really evil magic to initialize INSTALLED_APPS, database models and stuff and doing any kind of work during import causes headache.
So I figured out that you can apply monkey patches using middleware. Middleware applies the monkey patch when the first HTTP request hits the process (note that if you run preforked web server like FCGI every process has its own run-time code in memory). This technique, of course, cannot be used to monkey-patch things that happen before middleware processing, but it is not often needed.
Below is an example how to monkey-patch Django CMS to normalize its unicode output. There was an issue with unicode characters and this is a stop-gap measure to fix it. (I think the proper fix would be fix related Cufon font renderin Javascript library).
We add our monkey patcher to loaded middleware in settings.py.
The actual monkey patching happens by fiddling with the class code in process_request(). Note that in this particular case we only need to transform the output of the original function, we can simply hold a reference into it, call it and perform our transformation on the result. This way our monkey patch do not hinder the orignal function and is update safe (it does not matter if the code of PlaceholderNode.render method changes).
As far as I know, monkey patching is something PHP cannot do 🙂