10000 RFC Writing portable code · Issue #5564 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

RFC Writing portable code #5564

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
peterhinch opened this issue Jan 23, 2020 · 12 comments
Open

RFC Writing portable code #5564

peterhinch opened this issue Jan 23, 2020 · 12 comments
Labels
rfc Request for Comment

Comments

@peterhinch
Copy link
Contributor

With the increasing number of MicroPython targets, writing portable code has become problematic. An example in #5553 is the issue with the machine.RTC class, where available methods vary between platforms. Another example was where code tested on a Pyboard 1.x, Pyboard D, ESP8266 and ESP32 failed on a Pyboard Lite: alone among these platforms it lacks uos.urandom(), a fact missing from the uos docs.

There are also core language features which can be enabled or disabled at compile time.

The general question is how can you establish the set of platforms compatible with a given script? Empirical testing has obvious drawbacks. The approach of trawling through the build system (bearing in mind that release builds may differ from daily builds) is difficult. As the number of ports increases it becomes increasingly laborious; further the build system may be a moving target.

These potential solutions come to mind, but maybe there is a better way.

  1. Amend the docs for each new target. This seems hopelessly unwieldy, with each port generating a cascade of PR's and the docs becoming harder to read.
  2. Have a database which could be queried by users. Ideally this would be automatically updated: is this feasible?
  3. Define a reference build. This would include a defined set of features. In cases where the reference build does not support a feature, the official docs would state the fact - perhaps by a typographic convention. The build would be chosen to be fairly minimal with the hope that most ports would offer a superset. Each port would document its functionality with reference to that build. Program authors wanting portability would target that reference build, and could document their code accordingly.

The option of defining the reference build as an absolute minimum of functionality supported by all ports is too restrictive (e.g. no floating point).

@tve
Copy link
Contributor
tve commented Jan 23, 2020

Peter, thanks for starting this! I'm divided in that right now all I care about is ESP32, so I really don't give a hoot about compatibility, I just want to know what works there and what doesn't. And I'm OK adding what doesn't and I'm really happy I can do that without having to buy a board of every other platform so I can implement it there too in order to get a PR merged.

At the same time I'm not a maverick and agree that having more complete docs that break out what is supported where would help a lot. For one, it would actually make the differences visible. I'd read the docs and see that something isn't supported on the ESP32 and might spend the time to add it. The RTC situation, well, that's just appalling. But if the first implementation had been documented and there had been a hard rule that new additions (e.g. new ports) must update the docs I bet the problem would never have happened in the first place. (I strongly believe that making a hard rule thet any PR that wants to be merged must update docs is important: no doc update = no merge.)

In terms of technical solution, could the ..._dict_tables be made generic (not port specific) such that the ports either (1) have to implement the same functions or (2) use (highly visible and frowned upon) ifdefs in the dict tables, or (3) declare a function using a std macro like MP_NOT_IMPLEMENTED(machine_some_function) if the port doesn't support it. The macros ought to allow a pre-processing pass to produce a table of what's implemented where. Maybe this whole scheme could even be extended to the arguments in some way.

If there's a number of people willing to commit a bit of time to improve the docs as a group effort I'm in.

@peterhinch
Copy link
Contributor Author

Thorsten, thanks for your comments.

I strongly believe that making a hard rule thet any PR that wants to be merged must update docs is important: no doc update = no merge.

Agreed.

I'm happy to contribute to docs but I think option 1 is unmanageable. I'd like to see a technical solution which will solve the problem for current and future ports. The MicroPython project is enhanced by the availability of user-contributed code such as device drivers - it would be great if authors could state with confidence which platforms are supported.

I'm OK adding what doesn't and I'm really happy I can do that without having to buy a board of every other platform so I can implement it there too in order to get a PR merged.

I'm certainly not advocating that! There is a need to support port-specific methods, notably for hardware devices like the RTC. Using that example, basic methods such as (perhaps) datetime are required by all ports. Port-specific methods such as the ESP32 RTC.memory are also needed. It's perhaps a bit late in terms of breaking user code but this does seem like a classic case for subclassing.

@dpgeorge
Copy link
Member

In my opinion the problem can be split into two independent parts: core features configured by py/mpconfig.h, and standard-but-port-specific modules like machine. For the former, a port either enables it or not. For the latter the implementation is up to the port and it should be that standard functions/methods are equivalent across ports (I'm aware that this is not the case for all things).

Furthermore, the solution to the problem can be in the docs and/or implementation.


For features from py/mpconfig.h I think a good solution (first brought up by @jimmo IIRC..?), and as discussed above in point (3), is to define a few different reference configurations. Eg configuration A would be the most minimal (no floats, most things disabled, like bare-arm is now), configuration B would be small but useful, configuration C would be lots of things (eg current stm32, esp32), configuration D would be everything enabled (eg unix coverage build). Each bigger configuration would be a superset of the previous. Then a given driver/library/module/etc would target a minimum configuration level (eg if it targets level B then it runs on B, C and D).

We could provide unix builds at each level (eg micropython-dev-a, micropython-dev-b) to help test drivers/libraries/modules/etc.

Then a given port's mpconfigport.h file would be much smaller, it'd just set the configuration level instead of all the specific options.

Docs would list what is and is not available at each level, and a port's docs would say which level it supports.


For things like the machine module (eg machine.RTC), that just needs to be fixed to be consistent, and documented well.

@peterhinch
Copy link
Contributor Author
8000

This sounds like a good solution.

@rolandvs
Copy link
Contributor

Maybe the use of something similar as used in linux and zephyr being the menuconfig utility. It gives a list of all options (may be varied depending on the port, could use default settings and options can be explained with additional help. It can generate the necessary work to do to recompile mpy-cross, select the right options, generate make/make build instructions etc.

my2bits

@peterhinch
Copy link
Contributor Author

Most of my libraries aim to be used on any platform running a release binary. An aid to managing build options would be very useful, but it wouldn't actually address this problem.

@peterhinch
Copy link
Contributor Author
peterhinch commented Oct 8, 2021

I've fallen foul of this yet again. I published code which uses f-strings, with docs mandating firmware >= 1.17. Today I discovered that the code fails on an ESP8266. As far as I can see, nowhere is it documented that some platforms don't support f-strings.

Writing portable code is needlessly difficult - and you're left wondering what other features will barf on some platform or other.

@michel-eg
Copy link

Many useful things said here. But what stabds out to me ate the following issues:

  1. Target platforms and hardware availability.
    The list of target platforms for mpy is increasing, which is a good sign of its popularity. At the same time it is a problem for a volunteer based effort, as it increases the test coverage onus on developers if consistency across taegets is the goal.

  2. Compromise between consistency and hardware platform features
    The problem is exacerbated by the almost indefinite variability of platforms featuring the same SoC - look at the sea of products featuring the ESP8266.

  3. Documentation
    I used to be a developer, so I resonate with the thought of documentation being boring; let's tinker instead is all too appealing.
    Yet documentation is crucial for those who are new to anything - like me, being new to Python in general and micropython in particular.

Documentation is often the last resort to technical limitations. In short, in this instance: If you are inconsistent, DOCUMENT IT! This is often a matter of ownership and responsibility (for a piece of code).

  1. Oversight & strategy/policy
    I don't necessarily mean project oversight, more technical oversight in what is supported on which target platform.
    Once "we" know that, we can euther fix it, or document it.

I like the approach of coverage levels mentioned in an earlier post. I have no clue about the extent of language features in Python. But I think a coverage strategy along the lines of "what is core, what are modules, what is SoC specific, and what is platform specific?" might help here.

TL;DR

To be helpful I'd like to make a number of suggestions:

  1. Define language coverage policies, such as:
  • What is the baseline that must be supported on all targets?
  • For each SoC targeted, support all built-in features
  • For each platform supported, provide modules.for the platform's built-in hardware (e.g. some platforms have accelerometers others non't include)

Happy to help and discuss to find a good balance.

  1. Build an army of platform testers, increasing the coverage of available platforms as much as possible.

Seek to develop test coverage that is easy to run on platforms, gathering as much feedback on coverage and results as possible, as automated as possible. Then fan this out to your "army of platforms". I am sure many would consider joining if testing is easy and even perhaps relatively automated.

I'd be available to test on Expressif ESP8266 and ESP32 dev kits.

  1. Document the hell out of your project
    This is a tricky thing. Corporates are often addressing this by employing technical writers. Volunteer projects almost never find people who would be dedicated to that.

Maybe try and find a XP style of buddying up with some of the testers?

  1. Separate SoC support from platforms
    As indicated above, I think it might be useful to separate these two concepts,
    This would help defining what's core for micropython to support across SoCs (the actual targets), and what would needs to be added from a growing repository of (standard?) hardware features built-in on vsrious platforms.

  2. Improve firmware building infra.
    I particularly like the approach the NodeMCU community does it with Lua. Some very dedicated individual provides a build platform that makes it very easy to combine core Lua with a selection of modules to create a firmware specific for the ppatform they chose.
    This would address the current issue of esp8266 firmwares including modules for hardware specific to certain platforms (e.g. PyBoard).

I know I can fort the Github repo and start tinkering around to customise to what I want. But that is I think the right approach for those interested in developing mpy for a platform - but not for those who want to use it and develop ON TOP of of (like me).


A horribly long post, but I hooe it is useful in some ways.

@michel-eg
Copy link

P.S., the above was written on my mobile with "sausage fingers" akin to wearing mittens. So please excuse typos and other hiccups :-)

@dpgeorge
Copy link
Member
dpgeorge commented Nov 1, 2021

Today I discovered that the code fails on an ESP8266. As far as I can see, nowhere is it documented that some platforms don't support f-strings.

See #7956. IMO f-strings are really useful and we should support them on the main ports, because otherwise, as you say, it's impossible to write portable code that uses f-strings.

@dpgeorge
Copy link
Member
dpgeorge commented Nov 1, 2021

What is the baseline that must be supported on all targets?

That is pretty much what was started in 01374d9, which still needs further work.

@GadgetSteve
Copy link

Just to add a couple of cents worth, yes, documentation is very important and developers either hate doing it or leave it too late. But to add to the problem a great many developers hate _(re-)_reading documentation and only RTFM when things go wrong also too late.
I would suggest that extending a tool such as pylint with a micropython plugin so that developers can select which platform(s) they are interested in (with portable being all but obsolete) &/or selecting an implementation level as above would let developers check their code without necessarily having to build & test for each platform. The availability of each feature on each platform could then be updated by updating your "micro-pylint" or by it using a table that would be a part of each platform release.
Many IDEs support running pylint on each file save and displaying problems found.

The generation of the lookup tables, (and the updating of the documents), could be vastly aided by having a required set of features for all platforms that either must be implemented or marked with a NOT_IMPLEMENTED macro and possibly a platform developer mode for linting with errors for missing features would be nice but probably it would be easier to address this by the make process issuing warnings. (errors in release mode), for each feature that is not either implemented or marked as specifically NOT_IMPLEMENTED.

One thought might be to have more than none such flag, e.g. NOT_YET_IMPLEMENTED the developer plans to but hasn't yet, BETA_FEATURE not 100% tested use with caution, NO_HARDWARE_SUPPORT this platform lacks the supporting hardware to implement this.

kamtom480 pushed a commit to kamtom480/micropython that referenced this issue Nov 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfc Request for Comment
Projects
None yet
Development

No branches or pull requests

6 participants
0