Embedded python что это
Маленькие Python для маленьких embedded-программистов: CircuitPython и MicroPython для MeowBit
Осознавая это, в 2017 нью-йоркский стартап Adafruit начал разработку собственного форка MicroPython, получившего название CircuitPython. Эта реализация выполняет main.py каждый раз, когда файл с таким названием копируется через USB, и затем «обнуляет» среду, так что переменные из main.py не мешают ни REPL, ни следующему запускаемому скрипту. Чтобы остановить выполнение скрипта, достаточно удалить main.py через USB. В MicroPython нужно проявлять осторожность, чтобы во флеш-память не писали одновременно скрипт и компьютер через USB, иначе файловая система могла повредиться. В CircuitPython же перезапись main.py во время его выполнения – основной сценарий использования, так что ради предосторожности нужно выбрать один из двух вариантов – доступ ко флешу из Python только на чтение и через USB на чтение и запись, либо из Python на чтение и запись и через USB только на чтение, без возможности перезаписи либо удаления main.py. И ещё одна приятная фича – что до перехода в графический режим стандартный вывод дублируется и на последовательный интерфейс, и на экран. С другой стороны, MicroPython позволяет писать на Python обработчики прерываний (с рядом ограничений – например, в них нельзя создавать/удалять объекты на куче и нельзя бросать исключения), а CircuitPython – не позволяет, и не собирается добавлять такую
возможность.
Разница в подходе проявляется и в API. MicroPython стремится к краткости, CircuitPython – к абстракции:
Во втором примере разница в подходе наиболее наглядна: MicroPython предоставляет очень простую и прозрачную модель «создал массив значений пикселей, отправил его целиком на экран», навязывающую программисту довольно неудобный формат RGB565, потому что именно с таким форматом работает экран MeowBit (ST7735). Недостаток этой модели в том, что буфер 160х128х2 занимает 40 КБ – почти всю память, остающуюся свободной после загрузки MicroPython. Держать два таких буфера и отображать их поочерёдно – нет никакой возможности.
Один из моментов, вызванный разницей в подходах, хотелось бы разобрать подробнее: сложно представить игру без музыки и/или звуковых эффектов, но если на время проигрывания звуков игра будет приостанавливаться (как в примерах выше с delay и sleep ), то играть будет очень неудобно. Как же реализовать фоновый звук в двух вариантах Python?
Но теперь фоновый звук будет проигрываться не сам собой, а только при регулярном «дёрганьи» генератора. Это склоняет к тому, чтобы и остальные игровые процессы реализовать
в виде генераторов; например, заставка игры, прокручивающаяся вверх-вниз до нажатия любой
кнопки, реализуется следующим образом:
С одной стороны, реализация на MicroPython даёт заметно более качественный звук, потому что обработчик прерывания вызывается точно в заданное время, тогда как в CircuitPython на время перерисовки экрана (порядка 0.15 с) звук «подвисает». С другой стороны, код на CircuitPython легче писать, легче отлаживать и легче расширять, а реализация игровых процессов в виде сопрограмм-генераторов естественна и в отрыве от требований ко звуку.
Электрический блогнот
мои заметки на полях
python embedded или как добавить python в свое приложение
У Pyhon есть прекрасный инсталлятор, который все сдеает сам и установит Python со всеми стандартными модулями. Но, что делать, если ваше приложение использует python скрипты, а заставлять пользователя скачивать и устанавливать весь Python вам не хочется. Для этого существует Python Embedded (встраиваемый). Этот пакет не требует установки и может быть просто скопирован в папку с вашим приложением. Так же вы сможете установить все необходимые модули для работы и создать миниокружение для работы. Тем самым полностью избавить пользователя от лишних действий. Он даже и не узнает, что часть вашего приложения запускает Python. Этот прием я использовал в приложении Fpska (конвертация видео в 60 fps).
Далее я подробно распишу, как внедрить Python в свое приложение. Все эллементарно. Несколько простых шагов.
Шаг 1 — загружаем встраиваемый Python
Идем на python.org и скачиваем нужную версию python embedded:
Шаг 2 — устанавливаем встраиваемый Pyton
Вся установка сводится к простой распаковке архива:
На этом шаге можно было бы и остановиться, но чистый Python редко, кто использует. Нужны еще и модули. А чтобы поставить модули нужен pip (package installer for Python).
Шаг 3 — pip
Перед устанвкой pip настроим пути к библиотекам. Для этого в файле python37._pth нужно раскомментировать строку:
Скачиваем pip. Для этого рекомендуют использовать утилиту curl:
но можно просто скачать из браузера
Далее переходим в папку с embedded Python и устанавливаем инсталлятор пакетов (pip):
После установки pip появятся папки Lib и Scripts:
Сразу же проверим работает ли pip:
Шаг 4 — модули
Устанавливаем модули. Для примера установим модуль wxPython (добавляет графический интерфейс).
Шаг 5 — тестирование
Тестируем только что собранный Python. При тестировании очент важно проверить, что получился абсолютно независимый дистрибутив Python со всеми проинсталлированными модулями. Для этого устанавливаем все необходимые модули. Делаем архив папки, где установлен Python Embedded с модулями. И загружаем его куда-нибудь на файлообменник. Затем находим чистый Windows 10, где Python никогда не был установлен. Скачиваем архив и распаковываем. Запускаем любой тестовый скриптик. На следующей картинке тестовый запуск wxPython приложения:
1. Embedding Python in Another ApplicationВ¶
The previous chapters discussed how to extend Python, that is, how to extend the functionality of Python by attaching a library of C functions to it. It is also possible to do it the other way around: enrich your C/C++ application by embedding Python in it. Embedding provides your application with the ability to implement some of the functionality of your application in Python rather than C or C++. This can be used for many purposes; one example would be to allow users to tailor the application to their needs by writing some scripts in Python. You can also use it yourself if some of the functionality can be written in Python more easily.
Embedding Python is similar to extending it, but not quite. The difference is that when you extend Python, the main program of the application is still the Python interpreter, while if you embed Python, the main program may have nothing to do with Python — instead, some parts of the application occasionally call the Python interpreter to run some Python code.
The details of Python’s C interface are given in this manual. A great deal of necessary information can be found here.
1.1. Very High Level EmbeddingВ¶
The simplest form of embedding Python is the use of the very high level interface. This interface is intended to execute a Python script without needing to interact with the application directly. This can for example be used to perform some operation on a file.
1.2. Beyond Very High Level Embedding: An overviewВ¶
The high level interface gives you the ability to execute arbitrary pieces of Python code from your application, but exchanging data values is quite cumbersome to say the least. If you want that, you should use lower level calls. At the cost of having to write more C code, you can achieve almost anything.
It should be noted that extending Python and embedding Python is quite the same activity, despite the different intent. Most topics discussed in the previous chapters are still valid. To show this, consider what the extension code from Python to C really does:
Convert data values from Python to C,
Perform a function call to a C routine using the converted values, and
Convert the data values from the call from C to Python.
When embedding Python, the interface code does:
Convert data values from C to Python,
Perform a function call to a Python interface routine using the converted values, and
Convert the data values from the call from Python to C.
As you can see, the data conversion steps are simply swapped to accommodate the different direction of the cross-language transfer. The only difference is the routine that you call between both data conversions. When extending, you call a C routine, when embedding, you call a Python routine.
This chapter will not discuss how to convert data from Python to C and vice versa. Also, proper use of references and dealing with errors is assumed to be understood. Since these aspects do not differ from extending the interpreter, you can refer to earlier chapters for the required information.
1.3. Pure EmbeddingВ¶
The first program aims to execute a function in a Python script. Like in the section about the very high level interface, the Python interpreter does not directly interact with the application (but that will change in the next section).
The code to run a function defined in a Python script is:
then the result should be:
Although the program is quite large for its functionality, most of the code is for data conversion between Python and C, and for error reporting. The interesting part with respect to embedding Python starts with
Upon return of the function, pValue is either NULL or it contains a reference to the return value of the function. Be sure to release the reference after examining the value.
1.4. Extending Embedded PythonВ¶
Until now, the embedded Python interpreter had no access to functionality from the application itself. The Python API allows this by extending the embedded interpreter. That is, the embedded interpreter gets extended with routines provided by the application. While it sounds complex, it is not so bad. Simply forget for a while that the application starts the Python interpreter. Instead, consider the application to be a set of subroutines, and write some glue code that gives Python access to those routines, just like you would write a normal Python extension. For example:
Insert the above code just above the main() function. Also, insert the following two statements before the call to Py_Initialize() :
These two lines initialize the numargs variable, and make the emb.numargs() function accessible to the embedded Python interpreter. With these extensions, the Python script can do things like
In a real application, the methods will expose an API of the application to Python.
1.5. Embedding Python in C++В¶
It is also possible to embed Python in a C++ program; precisely how this is done will depend on the details of the C++ system used; in general you will need to write the main program in C++, and use the C++ compiler to compile and link your program. There is no need to recompile Python itself using C++.
1.6. Compiling and Linking under Unix-like systemsВ¶
The Owl Embedded Python System
The Owl Embedded Python System is a free and open-source system for programming small 32-bit microcontrollers in Python. It is dramatically easier to use than other programming environments for microcontrollers, while still powerful enough to build just about anything. Try it out today!
Owl is currently in a limited release. Right now, we have released VM binaries for TI Stellaris microcontrollers and the user toolchain as well as the complete source code. This is enough to get started programming microcontrollers, though there are more pieces yet to be released:
If you have any questions about Owl, contact us at twb@embeddedpython.org
Manifesto
Modern microcontrollers are almost always programmed in C. Applications run at a very low level without a real operating system. They are painfully difficult to debug, analyze, and maintain. At best, a simple real-time operating system (RTOS) is used for thread scheduling, synchronization, and communication. These systems provide primitive, low-level mechanisms that require expert knowledge to use and do very little to simplify programming. At worst, they are programmed on the bare metal, perhaps even without a C standard library. As the the electronic devices of the world become more and more complex, we absolutely have to do something to make embedded development easier.
We believe that the best way to do this is to run embedded software on top of a managed run-time system. We have developed and released as open-source an efficient embedded Python programming environment named Owl. Owl is a complete Python development toolchain and run-time system targeting systems that lack the resources to run a traditional operating system, but are still capable of running sophisticated software systems. Owl is a complete system, including an interactive development environment, a set of profilers, and an interpreter. It is derived from portions of several open-source projects, including CPython and Baobab. Most notably, the core run-time system for Owl is a modified version of Dean Hall’s Python-on-a-Chip.
Overall, we believe that Owl is the most productive, easy-to-use system in the world for programming small computers.
Installing Owl
The easiest way to learn about Owl is to dive in and get started. It’s easy and fun! First, install the command-line user utilities from the Python Package Index (PyPI) and easy_install. This should work out of the box on any system already running Python 2.7, including any recent version of Linux or OS X.
Next, make sure that you are using a microcontroller that is supported by Owl. Currently, the only chips supported are the TI Stellaris LM3S9x9x series. We’ve tested the platform on other chips and they will be supported soon. If you can’t wait that long, help us out by porting it!
Using Owl
Once you have Owl installed, you’ll want to get started. Owl is very similar to normal Python. If you know Python already, you should be just about ready to get started. If you don’t, it’s an easy language to pick up. The official Python tutorial is a great introduction, and most of what you read there should be applicable to Owl. Hordes of first-semester freshmen have learned their first Python (and generally their first programming) on Owl over the past few years, so you can too.
To connect to the microcontroller, you will use the mcu utility to start the interactive prompt.
Of course, just using a microcontroller as a computer isn’t much fun. We want to interact with the world. Owl comes with driver libraries to do exactly that. Take a look at the pages linked below to see what you can do.
Hacking with Owl
Owl is open-source, published on GitHub. It can be freely copied, modified and distributed. Let us know what you’re doing with it!
Hosted on GitHub Pages using the Dinky theme
IntroductionВ¶
The Application Programmer’s Interface to Python gives C and C++ programmers access to the Python interpreter at a variety of levels. The API is equally usable from C++, but for brevity it is generally referred to as the Python/C API. There are two fundamentally different reasons for using the Python/C API. The first reason is to write extension modules for specific purposes; these are C modules that extend the Python interpreter. This is probably the most common use. The second reason is to use Python as a component in a larger application; this technique is generally referred to as embedding Python in an application.
Writing an extension module is a relatively well-understood process, where a “cookbook” approach works well. There are several tools that automate the process to some extent. While people have embedded Python in other applications since its early existence, the process of embedding Python is less straightforward than writing an extension.
Many API functions are useful independent of whether you’re embedding or extending Python; moreover, most applications that embed Python will need to provide a custom extension as well, so it’s probably a good idea to become familiar with writing an extension before attempting to embed Python in a real application.
Coding standardsВ¶
If you’re writing C code for inclusion in CPython, you must follow the guidelines and standards defined in PEP 7. These guidelines apply regardless of the version of Python you are contributing to. Following these conventions is not necessary for your own third party extension modules, unless you eventually expect to contribute them to Python.
Include FilesВ¶
All function, type and macro definitions needed to use the Python/C API are included in your code by the following line:
Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included.
To include the headers, place both directories (if different) on your compiler’s search path for includes. Do not place the parent directories on the search path and then use #include
Useful macrosВ¶
Several useful macros are defined in the Python header files. Many are defined closer to where they are useful (e.g. Py_RETURN_NONE ). Others of a more general utility are defined here. This is not necessarily a complete listing.
Use this when you have a code path that cannot be reached by design. For example, in the default: clause in a switch statement for which all possible values are covered in case statements. Use this in places where you might be tempted to put an assert(0) or abort() call.
In release mode, the macro helps the compiler to optimize the code, and avoids a warning about unreachable code. For example, the macro is implemented with __builtin_unreachable() on GCC in release mode.
If a code path is very unlikely code but can be reached under exceptional case, this macro must not be used. For example, under low memory condition or if a system call returns a value out of the expected range. In this case, it’s better to report the error to the caller. If the error cannot be reported to caller, Py_FatalError() can be used.
Return the size of a structure ( type ) member in bytes.
Use this for unused arguments in a function definition to silence compiler warnings. Example: int func(int a, int Py_UNUSED(b)) < return a; >.
Use this for deprecated declarations. The macro must be placed before the symbol name.
Changed in version 3.8: MSVC support was added.
Creates a variable with name name that can be used in docstrings. If Python is built without docstrings, the value will be empty.
Use PyDoc_STRVAR for docstrings to support building Python without docstrings, as specified in PEP 7.
Creates a docstring for the given input string or an empty string if docstrings are disabled.
Use PyDoc_STR in specifying docstrings to support building Python without docstrings, as specified in PEP 7.
Objects, Types and Reference CountsВ¶
All Python objects (even Python integers) have a type and a reference count. An object’s type determines what kind of object it is (e.g., an integer, a list, or a user-defined function; there are many more as explained in The standard type hierarchy ). For each of the well-known types there is a macro to check whether an object is of that type; for instance, PyList_Check(a) is true if (and only if) the object pointed to by a is a Python list.
Reference CountsВ¶
The reference count is important because today’s computers have a finite (and often severely limited) memory size; it counts how many different places there are that have a reference to an object. Such a place could be another object, or a global (or static) C variable, or a local variable in some C function. When an object’s reference count becomes zero, the object is deallocated. If it contains references to other objects, their reference count is decremented. Those other objects may be deallocated in turn, if this decrement makes their reference count become zero, and so on. (There’s an obvious problem with objects that reference each other here; for now, the solution is “don’t do that.”)
Reference counts are always manipulated explicitly. The normal way is to use the macro Py_INCREF() to increment an object’s reference count by one, and Py_DECREF() to decrement it by one. The Py_DECREF() macro is considerably more complex than the incref one, since it must check whether the reference count becomes zero and then cause the object’s deallocator to be called. The deallocator is a function pointer contained in the object’s type structure. The type-specific deallocator takes care of decrementing the reference counts for other objects contained in the object if this is a compound object type, such as a list, as well as performing any additional finalization that’s needed. There’s no chance that the reference count can overflow; at least as many bits are used to hold the reference count as there are distinct memory locations in virtual memory (assuming sizeof(Py_ssize_t) >= sizeof(void*) ). Thus, the reference count increment is a simple operation.
It is not necessary to increment an object’s reference count for every local variable that contains a pointer to an object. In theory, the object’s reference count goes up by one when the variable is made to point to it and it goes down by one when the variable goes out of scope. However, these two cancel each other out, so at the end the reference count hasn’t changed. The only real reason to use the reference count is to prevent the object from being deallocated as long as our variable is pointing to it. If we know that there is at least one other reference to the object that lives at least as long as our variable, there is no need to increment the reference count temporarily. An important situation where this arises is in objects that are passed as arguments to C functions in an extension module that are called from Python; the call mechanism guarantees to hold a reference to every argument for the duration of the call.
Reference Count DetailsВ¶
Conversely, when a calling function passes in a reference to an object, there are two possibilities: the function steals a reference to the object, or it does not. Stealing a reference means that when you pass a reference to a function, that function assumes that it now owns that reference, and you are not responsible for it any longer.
Incidentally, PyTuple_SetItem() is the only way to set tuple items; PySequence_SetItem() and PyObject_SetItem() refuse to do this since tuples are an immutable data type. You should only use PyTuple_SetItem() for tuples that you are creating yourself.
It is much more common to use PyObject_SetItem() and friends with items whose references you are only borrowing, like arguments that were passed in to the function you are writing. In that case, their behaviour regarding reference counts is much saner, since you don’t have to increment a reference count so you can give a reference away (“have it be stolen”). For example, this function sets all items of a list (actually, any mutable sequence) to a given item:
TypesВ¶
ExceptionsВ¶
The Python programmer only needs to deal with exceptions if specific error handling is required; unhandled exceptions are automatically propagated to the caller, then to the caller’s caller, and so on, until they reach the top-level interpreter, where they are reported to the user accompanied by a stack traceback.
Exception state is maintained in per-thread storage (this is equivalent to using global storage in an unthreaded application). A thread can be in one of two states: an exception has occurred, or not. The function PyErr_Occurred() can be used to check for this: it returns a borrowed reference to the exception type object when an exception has occurred, and NULL otherwise. There are a number of functions to set the exception state: PyErr_SetString() is the most common (though not the most general) function to set the exception state, and PyErr_Clear() clears the exception state.
The full exception state consists of three objects (all of which can be NULL ): the exception type, the corresponding exception value, and the traceback. These have the same meanings as the Python result of sys.exc_info() ; however, they are not the same: the Python objects represent the last exception being handled by a Python try … except statement, while the C level exception state only exists while an exception is being passed on between C functions until it reaches the Python bytecode interpreter’s main loop, which takes care of transferring it to sys.exc_info() and friends.
As a general principle, a function that calls another function to perform some task should check whether the called function raised an exception, and if so, pass the exception state on to its caller. It should discard any object references that it owns, and return an error indicator, but it should not set another exception — that would overwrite the exception that was just raised, and lose important information about the exact cause of the error.
A simple example of detecting exceptions and passing them on is shown in the sum_sequence() example above. It so happens that this example doesn’t need to clean up any owned references when it detects an error. The following example function shows some error cleanup. First, to remind you why you like Python, we show the equivalent Python code:
Here is the corresponding C code, in all its glory:
Embedding PythonВ¶
The one important task that only embedders (as opposed to extension writers) of the Python interpreter have to worry about is the initialization, and possibly the finalization, of the Python interpreter. Most functionality of the interpreter can only be used after the interpreter has been initialized.
On most systems (in particular, on Unix and Windows, although the details are slightly different), Py_Initialize() calculates the module search path based upon its best guess for the location of the standard Python interpreter executable, assuming that the Python library is found in a fixed location relative to the Python interpreter executable. In particular, it looks for a directory named lib/python X.Y relative to the parent directory where the executable named python is found on the shell command search path (the environment variable PATH ).
Debugging BuildsВ¶
Python can be built with several macros to enable extra checks of the interpreter and extension modules. These checks tend to add a large amount of overhead to the runtime so they are not enabled by default.
A full list of the various types of debugging builds is in the file Misc/SpecialBuilds.txt in the Python source distribution. Builds are available that support tracing of reference counts, debugging the memory allocator, or low-level profiling of the main interpreter loop. Only the most frequently-used builds will be described in the remainder of this section.