Showing posts with label packaging. Show all posts
Showing posts with label packaging. Show all posts

So I wrote a Proof of Concept language to address the problem of safe eval

I told fellow coders: «hey! I know a solution to the safe eval problem: it is right under my eyes». I think I can code it in less than 24 hours from scratch. It will support safe templating... Because That's the primary purpose for it.


TL; DR:


I was told my solution was overengineering because writing a language is so much efforts. Actually it took me less time to write a language without any theorical knowledge than the time I have been loosing in my various jobs every single time to deal with unsafe eval.

Here is the result in python : a forth based templating language that does actually covers 90% of the real used case I have experienced that is a fair balance between time to code and real features people uses.


You don't actually need that much features.

https://github.com/jul/confined (+pypi package)

NB Work in progress

 

How I was tortured as a student


When I was a student, I was nicely helped through the hell of my chaotic studies by people in a university called ENS.

In exchange of their help I had to code for data measurement/labs with various language OS, and environment.

I was tortured because I liked programming and I did not have the right to do OOP, malloc, use new language .... Perl, python, new version of C standards...

Even for handling numbers scientifics were despising perl/python because of their inaptitude to safely handle maths. I had to use the «numerical recipies» and/or fortran. (I checked in 2005 they tried and were disappointed by python, I guess since then they might use numpy  that is basically binding on safe ports of numerical recipies in fortran). I was working on chaotic system that are really sensitive to initial conditions ... a small error in the input propagate fast.

The people were saying: we need this code to work and we need to be able to reuse it, and we need our output to be reproducible and verifiable : KISS. Keep It Simple Stupid. And even more stupid.

So I was barred from any unbound resource behaviour, unsafe behaviour with base types.

Actually by curiosity I recompiled code that was using C and piping output to tcl/tk I made at this time to make graphical representation of multi agent simulations and it still works... It was written in 1996.

That's how I learnt programming : by doing the worst possible unfunky programming ever.  I thought they were just stupid grumpy old men.

And I also had to use scientific equipment/softwares. They oddly enough all used forth RPN notations to enable users some basic manipulation.

Like:
  1. ASYST
  2. RRD Tools
  3. pytables NUMEPXR extension http://code.google.com/p/numexpr
And I realized I understood:

FORTH are easy to implement:
  • it is a simple left to right parsing technique: no backtracking/no states;
  • the grammar is easy to write; 
  • the memory model makes it easy to confine in boundaries;
  • it is immutable in its serialization (you can drop exec and data stack and safely resume/start/transport them)
  • it is thus efficient for parallization,
  • it thus can be used in embedded stuff (like measurement instruments that needs to be autonomous AND programmable)
 So I decide to give me one day to code in python a safe confined interpreter.

I was told it was complex to write a language especially when like I do, I never had any lessons/interests in parsing/language theory and I suck at mathematics.


Design choices


Having the minimum dependency requirements: stdlib.

 One number to rule them all 

I have been beaten so much time in web development by the floating point number especially for monetary values that I wanted a number that could do fixed point calculus. And also I have been beaten so many time by problems were the input were sensitive to initial conditions I wanted a number that would be better than IEEE 754 to potentially control errors.
So I went for the stdlib IEEE 854 officious standard based number : https://docs.python.org/2/library/decimal.html
Other advantages: string representation (IEEE 754) is canonical and the regexp is well known. Thus easy to parse.

In face of ambiguity refuse to guess

I will try to see input as (char *) and have the decoding being explicit.
Rationale: if you work with SIP (I do) headers are latin1 and if you work in an international environment you may have to face data incorrectly encoded that can also represent UTF8 and people in this place (Québec love to use accents éverywhere). So I want to use it myself.

It is also the reason I used my check_arg library to enforce type checking of my operators and document stuff by using a KISS approach: function names should be explicit and their args should tell you everything.

Having a modular grammar so that operators/base types can be added/removed easily. 


I evoked in a precedent post how we cannot do safe eval in python because keywords and cannot be controled. So I decided to have a dynamic grammar built at tokenization time (the code has the possibility to do it, it is not yet available through the API).

Avoid nested data structures recursive calls


I wanted to do a language my fellow mentors could use safely. I may implement recursive eval in the future but I will enforce a very limited level of recursion. But, I see a solution to replace nested calls by using the stack.

Stateless and immutables only


I have seen so many times people pickling function that I decided to have something more usable for remote execution. I also wanted my code to be idempotent. If parsing is seen as a function I wanted to guaranty that

parsing(Input, Environment) => output 

would be guaranteed to be always the same
We can also serialize the exec stack the data stack at any given moment to change it later. I want no side effects. As a result there will ne no time related functions.

As a result you can safely execute remote code.

Resource use should be controlled


Stack size, size of the input, recursion level, the initial state of the interpreter (default encoding, precision, number behaviours). I want to control everything (that what context will be for and all parameters WILL have to be mandatory). So that I can guaranty the most I can (I was thinking of writing C extensions to ensure we DONT use atof/atoi but strtol/f ...).

This way I can avoid to use an awful lot of virtual machines/docker/jails whatever.

Grammar should be easy to read


Since I don't know how to parse, but I love damian conway, I looked at Regexp::Grammar and I said: Oh! I want something like this.

There are numerous resource on stackoverflow on  how to parse exactly various base types (floats, strings). How to alternate and patterns... So that it took me 3 hours to imagine a way to do it. So I still know nothing of parsing and stuff, but I knew I would have a result.

I chose a grammar that can be written in a way to avoid backtracking (left to right helped a lot) to avoid the regexp to be uncontrolled.

I am not sure of what it does, but I am pretty sure it can be ported in C or whatever that guarantees NO nested/recursive use of resources. (regexp are not supposed to stay in a hardened version this is just a good enough parser written in 3 hours with my insufficient knowledge).

I still think Perl is right


We should do our unittest before our install. So my module refuse to install if the single actual test I put (as a POC) does not pass.


Conclusion


So it really worths the time spent. And now I may be in the «cour des grands» of the coders that implemented their own language, from scratch and without any prior theorical knowledge of how to write one. So I have been geeking alone in front of my computer and my wife is pissed at me for not enoying the day and behaving like an autist, but I made something good enough for my own use case.

And requirements with python and making tests before install is hellish.

(Arg ... And why my doc does not show up on pypi? )

Making tests before installation with setuptools

I dream that packages don't install if the tests are failing. I made it at least.

My solution is gory but practical.

in setup.py I added:

def test():
    """let's script the command line python -munittest discover"""
    loader= unittest.TestLoader()
    suite=loader.discover(".", "test_.*.py")
    runner=unittest.TextTestRunner()
    result=runner.run(suite)
    if  not result.wasSuccessful():
        raise Exception( "Test Failed: Aborting install")
    print("#### Test passed")

if "install" in sys.argv or "bdist_egg" in sys.argv or "sdist" in sys.argv:
    test()


And since practicality beats purity...

Still wondering if it is a bad idea



Okay, I test my packages before deploying, okay, there is tox. But no test can make as much variations as what users have as an environment. Even though I don't find it nice, at least it has the authoritative psycho-rigid behaviour I want.

Just like in Perl, if it does not pass the tests, it should not be installed.

I still wish I could:
  • make an autoreport tool (calling a REST server) to know how reliable are my packages, and which OSes/python versions have problems;
  • have a tool that let user interract with ticketting system;
  • bypass the tests with a --force flag, and call the test suite in a unified way once the package is installed.
 Still dreaming.

Packaging in python from a former Perl dev point of view

It's easy


I use github, readthedocs post commit hooks,  http://guide.python-distribute.org/ and it all works fine: I am delighted to be honest.

As far as I am concerned packaging in python is freaking easy, a fun and rewarding experience. So here is my point: this is not a rant since I love packaging in python.

However, to be honest there are quite a few things that trouble me:
  • I don't really know what I am doing since I mostly follow cookbooks;
  • I am lacking some of the CPAN features;
  • and I do have (the maybe wrong feeling) that the packaging culture amongst python community is not as strong as in Perl (this also applies to my production).

from CPAN import wisdom

Most of Perl's quality module are not coming from PEP they are coming from cultural habits. I dare cut and paste some of tutorial for perl


  • Write the documentation for a module first, before writing any code. Discuss the module with other people first, before writing any code. Plan the module first, before writing any code.
  • It's easy to come up with a solution to a problem. It takes planning to come up with a good solution. Remember: the documentation, not the code, defines what a module does.
  • Every module should have a purpose. There's a proliferation of modules with names like "perlutils.pm", "rcs_utils.pm", and "utilUtils.pm" that have no obvious purpose, and it's difficult to know what each does. This leads to confusion and duplication of code.
Well, since we are plagued on pypi with the infamous «nested list printer» and ports of PHP file_get_contents, I guess this wisdom is not yet totally in python.

I think python tutorial focus too much on the technical part (how to build a package) and not enough on the QA part (how to make a useful and maintainable package).

There is also something in CPAN I love, and I don't follow because I lost myself in geeking with sphinx, it is the straightforward documentation in one page following this plan:
  • Name 
  • Version
  • Synopsis (short code snippet that works)
  • Description (in full english with no code)
  • Methods (with code snippets if useful)
  • Notes (extra informations needed)
  • See also (similar packages)
  • Limitations
  • Bugs
  • Author
  • Licence
It is a kind of very informative plan.

These are what the culture provides. Plans are not imposed to the packager, it just converged as being efficient.

from CPAN import tools


Now, CPAN has also great tools we miss in python: for instance prior to installation there are the automated tests and eventually automated reports.

I do always test a package before pushing it, but, I'd rather force tests that prevent installation if they fail on the user side. I tried ditribute test suites feature, but I fumbled.

You know what, I miss this feature, and the deployment matrix.

I miss to see if maintainer is active by being able to watch its ticketing queue. The ticketing system is in CPAN.

I also miss the direct link to the source. Or the dependencies chart.

I also like how they handle the «Missing In Action» of packagers and how they can decide to hand over a package's maintenance to another maintainer.

This have very few chances to happen in a close future. However, I can see how we can all improve our package.

from packager import good_will


Good news is we don't need code to solve most of these problems.

Documentation


In the README I provide with my packages I (try to) include:
  • a link to the sources
  • a link to the full documentation (on readthedocs and package.python.org)
  • a link to the ticketing of github
  • a synopsis
  • requirements (in case my dependencies get weirdly not computed)
  • a changelog
I will try from now on to be terser in my documentation and follow the previously mentioned Perl plan.

I noticed repoze.lru changed its former nice README for a useless one. I am sad.




Testing

 
Always test before pushing. It may seem obvious, but I have noticed some maintainers don't. Build a sdist, make a clean virtualenv with nothing to install your package before uploading it too. It is nice.

Versioning


Follow http://www.python.org/dev/peps/pep-0386/

Always tag your source code in your repository with the adequate pypi version.


Don't ask GvR what you can achieve by yourself


First I understand nothing of the mess between distribute, distribute2, setuptools, so I gave up dreaming of hacking my way to a solution through brute coding. And I guess you don't make a donkey that is not thirsty drink water.

My workaround is as a packager I can by showing the example and hope people will follow me improve python packaging with my own lever: practice and culture.

In the ecosystem I am not only a producer I am also a consumer of packages. So I think that as consumers of packages YOU can also improve the packaging ecosystem by checking that a package follows most of these rules before installing it:

  • is the README on pypi including 
    • a link to the source, 
    • the ticketing system, 
    • a synopsis, 
    • a changelog, 
    • a link to the the full documentation (1 point per present info);
  • is the full documentation following the Perl canonical Plan (in regard to the complexity of the package don't be too picky (5 points if the doc is relevant))
  • does the source code contains a test suite? (5 points)
  • can I reach the maintainer IRL (2 points)
  • are there outstanding issues in the ticketing (2 points if all issues are opened for less than 1 month)
  • is there an auto reporting tool in the package (that triggers the test suite and submit it to a ticketing system) (5 points (I won't have them)). 
  • versioning the PEP way (3 points)
In my case I won't use a package having less than 20 points on my own scale. If we all do that we have a chance that packaging improves. I guess Perl has made mistakes so I really don't advocate following their steps blindly, I advocate that we do also slowly build our own strong culture of Quality Assurance the python way.

Oh, and since I am proud of finalizing this «Book» (perl dev) snippet, here is a polyglot in Perl and python to do i++ and ${A}++

q = 0 or """ #=;$A=41;sub A { ~-$A+2}; A() && q' """
A=lambda A: -~A #';
print A(41) # python + perl = <3

Small tips to improve your packages on pypi (for beginners)

I am a big fan of plots. Not for their beauty, but for their dense informational properties. And since I capture download time series on pypi downloads on miscellaneous packages here are my preliminary findings (that needs validations, since my samples are not relevant in terms of size):

  1. After the first release you have 8 days to find your public;
  2. make packages with practical use;
  3. while new releases will reboost your exposure, if you have found your adoptants it will not make miracles (don't spam pypi, it is useless);
  4. README might be the most relevant criteria for early adoption; 
  5. put some actual code use in your README that is revelant.

Here are some funny stuffs I have no way (and not enough knowledge) to check and I dream to have an answer for: 
  1. the impact of the quality of the setup (have you filled in properly your setup, your trove classifiers);
  2. quality measure for documentation and impact on the downloads (I like pathlib's doc better than mine, because it sticks to the facts);
  3. snowball effect due to the reputation of the packager;
  4. is there an optimal templates for doc? (can we correlate a doc structure to a better adoption?)
  5. the impact of documentation presence (either pypi or rtd) on package adoption (this one seems obvious to me);
  6. the  impact of alpha/beta/stable tagging on adoption;
  7. the impact of a source code link in the README, and of a valid home page;
  8. which kind of home page increases adoption? 
  9. what is the optimal number of functionalities (number of class/methods) for a package (is sparse better than dense, simple better than complex) ? 
  10. which metrics are the most significant?

The purpose of the exercise is not to tell how smart I am, but to daydream of some feedbacks  from the QA in the packaging guide or in distribute (like an enhanced python setup.py check). I do lack skills to do all the aforementioned tests so I mainly send a message in a bottle expecting it to drift one day on the shore of the pypi/distribute team :) 

PS : I miss the make test from Perl and I can't figure a way to make my unittest mandatory prior to the installation of my package, and to have an automated feedback when pip install fails to improve my packaging. As a result if pypi would state the percentage of failed install over successful install I would be delighted.

Fixed window of ~1 week for finding your public



If we consider all the new packages I monitored (gof, archery, weirddict) you'll notice a charging law in the form of DLmax(1-e(-time/4d)) + adoption_rate*time.

ex :

Possible Explanation: 



RSS feeds will propagate, and your package will be visible for this period on various places and on sites such as https://pythonpackages.com/.

How to validate



For any new package : the ratio of download  after 4 days and 8 days should be the same. And the ratio after 16 days / 8 days should be far less than 4 days / 8 days. Most non adopted packages should have a flat download curve.


Impact


What if packages older than x years without adoptants were cleaned of pypi?

A package that meet its public will have an almost linear growth




Explanation



Well on this one it is just observation :)

How to validate


For any download curve that don't follow the DLmax(1-e(-t/4d)) do the diff after 16 days for period without releases and then check the diff is constant +-10%.


A new release will give you a new chance in adoption


 A new release gives you a second chance but is is needless to spam pypi with new releases since your rate of adoption won't normally change.

Explanation



Your new release will encourage migration anyway on one hand, but on the other hand if you have met your demand, then your «adoption market» is already saturated.

Validation


Do the linear diff of the curves 10 days before and after a release on a package and check that the growth ratio is the same.

Practicality beats purity



Given two packages : VectorDict (I recommend archery instead) for dict with addition and pypi-stat (used for these graphs) the practical package will have more chance of adoption than the more abstract one.









 

As you can see, VectorDict adoption is mostly due to pypi-stat adoption (200dl/4weeks).



 Explanation



Your fellow coder search pypi for actual solutions to their problems not for a tool in search for a solution. Coders better understand practical use than conceptual ones.


Validation


If you have a configuration with one «abstract package» and one or several «practical package» depending on it subtract the download curve of the practical package from the «abstract» one. I bet growing curve of abstract package is less that practical one. 



Put Some practical code in your README


The new curve for archery has a better growth. The difference is I put some use code. My intuition is putting actual code in the README helps

Validation



Parsing README and measuring downloads after 16 days for the 1st release in two groups : the one with code in it, and the ones without. There should be a bimodal distribution if I am right.