Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env
data.json
makefile
*.service
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
See README files in respective folders.
37 changes: 2 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,3 @@
A summary of package management tools in the Python ecosystem
An interactive web app to explore, and hopefully better understand, Python packaging tools.

## tools

| | poetry | pipenv | pip | virtualenv | setup.py (setuptools) | twine | pyenv | pipsi |
|--------------------------------------------------------------------------------------------|------------------------------------------|----------------------------------------------------|--------------------------|------------------------------------|------------------------------------------------------------------|---------------------------------------------------------|---------------------------|--------------------------------|
| installs abstract dependencies | ✔️ | ✔️ | ✔️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ |
| installs concrete dependencies | ✔️ | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ |
| isolates Python environments | ✔️ | ✔️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ | ✔️ |
| captures packaging metadata (name, version, author, etc) | ✔️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ |
| file format based on a PEP | ✔️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ |
| automatically creates virtual environments | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ |
| generates concrete dependencies from abstract dependencies (requirements.txt or lock file) | ✔️ | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ |
| can generate lock file with hashes | ✔️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ |
| builds packages suitable for distribution on PyPI | ✔️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ |
| publishes packages to PyPI | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ | ✖️ |
| can enter shell for virtual environment | ✖️ | ✔️ | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ |
| installs specific Python versions to system | ✖️ | ✔️ | ✖️ | ✖️ | ✖️ | ✖️ | ✔️ | ✖️ |
| globally installs and sandboxes Python packages that have command-line entry points | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✖️ | ✔️ |
| relies on | virtualenv | virtualenv, pew, pip-tools, requirementslib, pyenv | - | pip, shell scripts | setuptools | - | shell scripts | virtualenv |
| stated purpose | dependency management and packaging tool | development workflow tool | Python package installer | Python virtual environment builder | download, build, install, upgrade, and uninstall Python packages | Collection of utilities for publishing packages on PyPI | python version management | global Python script installer |


## File formats

| | requirements.txt | Pipfile | Pipfile.lock | pyproject.toml | pyproject.lock |setup.py
|-------------------|-------------------|----------------------|----------------------|----------------|----------------------|-|
| purpose | todo | | | | | |
| metadata captured | todo | | | | | |
| origin | convention of pip | convention of Pipenv | convention of Pipenv | PEP 518 | convention of Poetry | todo |



Disclaimer: I did not author or maintain any of these tools and strive to keep this list as impartial as possible.

This information is accurate to the best of my knowledge. If anything is incorrect or missing please submit a pull request or create an issue.
https://grassfedcode.com/python-packaging
11 changes: 11 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Backend

## Getting Started
Install dependencies:
```
> yarn
```
Start server:
```
> yarn start
```
116 changes: 116 additions & 0 deletions backend/githubData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Octokit } from "@octokit/core";
import * as dotenv from "dotenv";
import * as fs from "fs";

const env = dotenv.config().parsed;
const apiKey = env.API_KEY;
const octokit = new Octokit({ auth: apiKey });

const tools = [
{
name: "pip",
owner: "pypa",
},
{
name: "tox",
owner: "tox-dev",
},
{
name: "nox",
owner: "theacodes",
},
{
owner: "pipxproject",
name: "pipx",
},
{
name: "poetry",
owner: "python-poetry",
},
{
name: "pipenv",
owner: "pypa",
},
{
name: "virtualenv",
owner: "pypa",
},
{
name: "pipenv",
owner: "pypa",
},
{
name: "pyenv",
owner: "pyenv",
},
{
name: "twine",
owner: "pypa",
},
{
name: "setuptools",
owner: "pypa",
},
{
name: "pip-tools",
owner: "jazzband",
},
];

function getQuery(name: string, owner: string) {
return `query {
repository(owner: "${owner}", name: "${name}") {
stargazers {
totalCount
}
pushedAt
createdAt
description
name
issues {
totalCount
}
licenseInfo {
name
}
primaryLanguage {
name
}
url
homepageUrl
}
}`;
}

export async function fetchGithubData() {
// try {
// console.error("Attempting to read data");
// return JSON.parse(String(fs.readFileSync("./data.json")));
// } catch (e) {
// console.error(e.message);
// console.error("Failed to read, fetching...");
const data = (
await Promise.all(
tools.map((t) => {
return octokit.graphql(getQuery(t.name, t.owner));
})
)
).map((d) => d["repository"]);
fs.writeFileSync("./data.json", JSON.stringify(data, null, 4));
return data;
// }
}

async function main() {
const response = await fetchGithubData();
console.error(response);
}

// (async () => {
// try {
// var text = await main();
// console.log(text);
// } catch (e) {
// console.error(e);
// }
// })();
21 changes: 21 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "backend",
"version": "1.0.0",
"scripts": {
"start": "rm ./data.json; ts-node server.ts"
},
"main": "index.js",
"license": "MIT",
"dependencies": {
"@octokit/core": "^3.1.2",
"@types/dotenv": "^8.2.0",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"ts-node": "^9.0.0",
"typescript": "^4.0.2"
},
"devDependencies": {
"@types/express": "^4.17.8",
"@types/node": "^14.6.4"
}
}
41 changes: 41 additions & 0 deletions backend/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as express from "express";
import { fetchGithubData } from "./githubData";
import * as path from "path";
const app = express();
const port = process.env.PORT ?? 3001;

const secPerMin = 60;
const minPerHour = 60;
const msPerSec = 1000;
const hourInMs = secPerMin * minPerHour * msPerSec;
let cachedData: { cachedAt: number; data: any } = {
cachedAt: 0,
data: null,
};
const maxCacheAgeMs = hourInMs;

const msToMinutes = (1 / msPerSec) * (1 / secPerMin);

app.get("/package_data", async (req, res) => {
const now = Date.now();
const age = now - cachedData.cachedAt;
const timeUntilRefetch = maxCacheAgeMs - age;
console.error(
`Cache is ${age * msToMinutes} minutes old. ${
timeUntilRefetch * msToMinutes
} minutes left until new data is used`
);
if (timeUntilRefetch <= 0) {
cachedData.data = await fetchGithubData();
cachedData.cachedAt = now;
} else {
console.error("using cached data");
}
res.json(cachedData.data);
});

app.use("/", express.static(path.join(__dirname, "..", "frontend", "build")));

app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
3 changes: 3 additions & 0 deletions backend/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#/bin/bash
yarn
yarn start
Loading