Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
Developer Ergonomics
José Padilla
February 18, 2017
Programming
0
1.9k
Developer Ergonomics
Keynote for PyCaribbean
José Padilla
February 18, 2017
Tweet
Share
More Decks by José Padilla
See All by José Padilla
jpadilla
0
12
jpadilla
0
4.1k
jpadilla
0
130
jpadilla
15
9.5k
jpadilla
4
150
jpadilla
0
120
jpadilla
2
330
jpadilla
3
2.1k
jpadilla
2
150
Other Decks in Programming
See All in Programming
nbkouhou
0
960
masayaaoyama
1
260
jun0
3
660
takahi5
0
230
yshrsmz
1
460
viteinfinite
0
200
line_developers_tw2
0
740
akatsukinewgrad
0
210
decoc
1
340
hanhan1978
0
300
ken3ypa
0
160
itosho525
0
150
Featured
See All Featured
smashingmag
229
18k
lauravandoore
11
1.3k
chriscoyier
780
240k
roundedbygravity
84
7.8k
lara
172
9.5k
jensimmons
207
10k
malarkey
393
60k
mongodb
23
3.8k
philnash
8
500
brad_frost
156
6.4k
phodgson
87
3.9k
paulrobertlloyd
71
3.6k
Transcript
Problem
You'll eventually want to install third party packages...
Enter easy_install
Enter easy_install
Enter pip
$ pip install Django==1.8
You just installed Django globally.
$ pip install Django==1.10
You just installed Django globally.
Developer Ergonomics
Package Managers and Environments
José Padilla
Work Training Open Source
Work Training Open Source
Work Training Open Source
Backstory
Lets begin...
Node.js + NPM
Node Package Manager
Installing packages with npm is easy.
$ npm install express
└─┬ express@4.14.1 ├─┬ accepts@1.3.3 │ ├─┬ mime-types@2.1.14 │ │ └──
mime-db@1.26.0 │ └── negotiator@0.6.1 ├── array-flatten@1.1.1 ├── content-disposition@0.5.2 ├── content-type@1.0.2 ├── cookie@0.3.1 ├── cookie-signature@1.0.6 ├─┬ debug@2.2.0 │ └── ms@0.7.1 ├── depd@1.1.0 ├── encodeurl@1.0.1 ├── escape-html@1.0.3 ├── etag@1.7.0 ├─┬ finalhandler@0.5.1 │ ├── statuses@1.3.1 │ └── unpipe@1.0.0 ├── fresh@0.3.0 ├── merge-descriptors@1.0.1 ├── methods@1.1.2 ├─┬ on-finished@2.3.0 │ └── ee-first@1.1.1 ├── parseurl@1.3.1 ├── path-to-regexp@0.1.7 ├─┬ proxy-addr@1.1.3 │ ├── forwarded@0.1.0 │ └── ipaddr.js@1.2.0 ├── qs@6.2.0 ├── range-parser@1.2.0 ├─┬ send@0.14.2 │ ├── destroy@1.0.4 │ ├─┬ http-errors@1.5.1 │ │ ├── inherits@2.0.3 │ │ └── setprototypeof@1.0.2 │ ├── mime@1.3.4 │ └── ms@0.7.2 ├── serve-static@1.11.2 ├─┬ type-is@1.6.14 │ └── media-typer@0.3.0 ├── utils-merge@1.0.0 └── vary@1.1.0 └─┬ express@4.14.1 ├─┬ accepts@1.3.3 │ ├─┬ mime-types@2.1.14 │ │ └── mime-db@1.26.0 │ └── negotiator@0.6.1 ├── array-flatten@1.1.1 ├── content-disposition@0.5.2 ├── content-type@1.0.2 ├── cookie@0.3.1 ├── cookie-signature@1.0.6 ├─┬ debug@2.2.0 │ └── ms@0.7.1 ├── depd@1.1.0 ├── encodeurl@1.0.1 ├── escape-html@1.0.3
$ npm install express --save
{ "name": "pycaribbean", "version": "1.0.0", "dependencies": { "express": "^4.14.1" }
}
$ npm uninstall express --save
{ "name": "pycaribbean", "version": "1.0.0", "dependencies": {} }
The default behavior of npm is to install packages locally
to the current directory.
global = ?
global = CLI tools
$ npm install eslint --global
Dev / Prod
$ npm install supertest --save-dev
{ "name": "nodejs", "version": "1.0.0", "dependencies": {}, "devDependencies": { "supertest":
"^3.0.0" } }
$ npm install
$ npm install --production
Dependency Conflicts
$ npm install package-a --save
└─┬ package-a@1.0.0 └── request@1.0.0
$ npm install package-b --save
└─┬ package-b@1.0.0 └── request@2.0.0
├─┬ package-a@1.0.0 │ └── request@1.0.0 └─┬ package-b@1.0.0 └── request@2.0.0
Determinism
None
Determinism
$ npm shrinkwrap
{ "name": "A", "version": "1.1.0", "dependencies": { "B": { "version":
"1.0.1", "from": "B@^1.0.0", "resolved": "..." } } }
Node.js + Yarn
Yarn
Fast
Reliable
Secure
Installing packages with yarn is easy.
$ yarn
$ yarn add express
$ yarn add --dev mocha
$ yarn global add eslint
Determinism
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS
FILE DIRECTLY. # yarn lockfile v1 package-1@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/package-1/-/ package-1-1.0.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c2 84ca" package-2@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/package-2/-/ package-2-2.0.1.tgz#9a5f699051b1e7073328f2a008968b64ea29 55d2" dependencies: package-4 "^4.0.0"
Rust + Cargo
Cargo
Cross-pollination at its best.
Cargo.toml
[package] name = "package_a" version = "0.1.0" authors = ["root"]
[dependencies] package_b = "0.0.1"
Cargo.lock
[root] name = "package_a" version = "0.0.1" dependencies = [
"package_b 0.0.1 (...)", ] [[package]] name = "package_b" version = "0.0.1" source = "..." [metadata] "checksum package_b 0.0.1 (...)" = "7507624b29483431c0ba2d82aece"
Python + PIP
PIP
Installing packages with pip is easy.
$ pip install flask
$ pip install flask $ pip freeze > requirements.txt
click==6.7 Flask==0.12 itsdangerous==0.24 Jinja2==2.9.5 MarkupSafe==0.23 Werkzeug==0.11.15
$ pip uninstall flask $ pip freeze > requirements.txt
click==6.7 itsdangerous==0.24 Jinja2==2.9.5 MarkupSafe==0.23 Werkzeug==0.11.15
Dev / Prod
├── requirements │ ├── common.txt │ ├── dev.txt │ └──
prod.txt └── requirements.txt
$ cat requirements.txt -r requirements/prod.txt
$ cat requirements/prod.txt -r common.txt gunicorn==19.6.0 newrelic==2.74.0.54
$ cat requirements/common.txt Django==1.10.5
$ cat requirements/dev.txt pytest==3.0.6
$ pip install -r requirements.txt
$ pip install -r requirements/dev.txt
The default behavior of pip is to install packages globally.
Installing local dependencies
Python 2
virtualenv
$ pip install virtualenv
$ virtualenv .venv
$ source .venv/bin/activate
$ .venv/bin/python Python 2.7.9 (default, Dec 21 2016, 01:15:20) [GCC
4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>>
(.venv) $ which python /path/to/.venv/bin/python
(.venv) $ pip install flask
(.venv) $ deactivate
$ which python /usr/bin/python
Python 3
venv
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ pip install flask
(.venv) $ deactivate
Dependency Conflicts
package-a Depends on requests==1.0.0
package-b Depends on requests==2.12.4
$ pip install package-a $ pip install package-b $ pip
freeze
package-a==0.1 package-b==0.1 requests==2.12.4
Packages and dependencies are flat.
$ pip check package-a has requirement requests==1.0.0, but you have
requests 2.12.4.
$ pip install flask && pip check
function pip-install() { pip install $@ && pip check }
$ pip-install flask
Going forward
Changing the defaults of tools like pip without creating chaos
is hard.
Installing packages in Python could be easier.
"One of the hurdles that new Python developers have to
get over is understanding the Python packaging ecosystem." – Jamie Matthews (@j4mie)
"Variables in Python are local by default, so why aren't
packages? You have to go out of your way to make a global variable. You should also have to go out of your way to install a package globally." – Trey Hunner (@treyhunner)
My ideal scenario
Default to installing packages locally.
Improved dependency resolving.
Fully specified and deterministic.
Secure.
$ pip init
$ pip install flask
$ pip install --dev pytest
$ pip install
How does it work?
IDK
"There's someone already working on a similar idea"
Kenneth Reitz open sourced pipenv on January 19 2017
Python + Pipenv
More than 15 hours on Hacker News' front page.
More than 2500 stars on GitHub.
Dogfooding kennethreitz/requests
Pipenv
Sacred Marriage of Pipfile, Pip, & Virtualenv
Cross-pollination at its best.
Pipfile
Requirements 2.0
Pipfile will be superior to requirements.txt file in a number
of ways...
TOML syntax for declaring all types of Python dependencies.
One Pipfile, as opposed to multiple requirements.txt files.
[dev-packages] pytest = "*" [packages] flask = "*"
Pipfile.lock
{ "default": { "MarkupSafe": { "version": "==0.23", "hash": "sha256:a4ec1aff59b95a1..." },
"Jinja2": { "version": "==2.9.5", "hash": "sha256:a7b7438120dbe76..." }, "Werkzeug": { "version": "==0.11.15", "hash": "sha256:c6f6f89124df051..." }, "flask": { "version": "==0.12", "hash": "sha256:7f03bb2c2554524..." }, "itsdangerous": { "version": "==0.24", "hash": "sha256:cbb3fcf8d3e33df..." }, "click": { "version": "==6.7", "hash": "sha256:29f99fc6125fbc9..." } }, "develop": { "packaging": { "version": "==16.8", "hash": "sha256:99276dc6e3a7851..." }, "pytest": { "version": "==3.0.6", "hash": "sha256:da0ab50c7eec068..." }, "setuptools": { "version": "==34.1.1", "hash": "sha256:5f74aabe68c441b..." }, "pyparsing": { "version": "==2.1.10", "hash": "sha256:67101d7acee6929..." }, "py": { "version": "==1.4.32", "hash": "sha256:2d4bba2e25fff58..." }, "six": { "version": "==1.10.0", "hash": "sha256:0ff78c403d9bccf..." }, "appdirs": { "version": "==1.4.0", "hash": "sha256:85e58578db8f295..." } }, "_meta": { "sources": [ { "url": "https://pypi.python.org/simple", "verify_ssl": true } ], "requires": {}, "hash": { "sha256": "c755a9d5787ce2fdc70f7..." } } } { "default": { "MarkupSafe": { "version": "==0.23", "hash": "sha256:a4ec1aff59b95a1..." }, "Jinja2": { "version": "==2.9.5", "hash": "sha256:a7b7438120dbe76..." }, "Werkzeug": { "version": "==0.11.15", "hash": "sha256:c6f6f89124df051..." }, "flask": { "version": "==0.12", "hash": "sha256:7f03bb2c2554524..." }, "itsdangerous": { "version": "==0.24", "hash": "sha256:cbb3fcf8d3e33df..." }, "click": { "version": "==6.7", "hash": "sha256:29f99fc6125fbc9..." } }, "develop": { "packaging": { "version": "==16.8", "hash": "sha256:99276dc6e3a7851..." },
pypa/pipfile
Pipenv
Enables truly deterministic builds, while easily specifying what you want.
Automatically generates and checks file hashes for locked dependencies.
Automatically finds your project home, recursively, by looking for a
Pipfile.
Automatically generates a Pipfile, if one doesn’t exist.
Automatically generates a Pipfile.lock, if one doesn’t exist.
Automatically creates a virtualenv in a standard location.
Automatically adds packages to a Pipfile when they are installed.
Automatically removes packages from a Pipfile when they are un-installed.
Also automatically updates pip.
Installing packages with Pipenv is easy.
$ pipenv install flask
$ pipenv install pytest --dev
$ pipenv install
$ pipenv shell
Current drawbacks?
$ pipenv install flask $ pipenv lock
$ pipenv install --lock flask
pipenv.org
Conclusion
Its our job to build the best software possible using
the best tools for it.
We go out there and learn from other communities to
make ours better.
Python is not only for those that've been using it
for the past 10 years...
...its also for those that will begin using it for
the next 10.
There are smart people already working on how to improve
this.
Awesome work already done.
Thank you to everyone helping make Python and it's ecosystem
better.
From The Zen of Python
Beautiful is better than ugly.
Simple is better than complex.
There should be one and preferably only one obvious way
to do it.
Now is better than never.
Thank you