Skip to content

Commit f78c896

Browse files
committed
Add PyPI integration
1 parent acb54ea commit f78c896

File tree

7 files changed

+141
-0
lines changed

7 files changed

+141
-0
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
- GitHub
2727
- crates.io
2828
- npm
29+
- PyPI
2930

3031
<small>More integrations coming soon!</small>
3132

@@ -43,6 +44,12 @@ Building SpaceBadgers has been a labor of love, aiming to offer a superior, reli
4344

4445
## Development
4546

47+
Clone the repository and make sure to initialize the submodules
48+
49+
```bash
50+
git submodule update --init --recursive
51+
```
52+
4653
### Environment Variables
4754
> Paste this template into `badgers-web/.env.local`
4855

badgers-web/src/app/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ export default function Home() {
180180
<Row name="Types" path="/npm/types/:pkg" inject={['react']} />
181181
<Row name="Types (scoped)" path="/npm/types/:org/:pkg" inject={['@octokit', 'rest']} />
182182
</div>
183+
</Section>
184+
<Section name="PyPI">
185+
<div className="grid grid-cols-[auto_1fr_auto] items-center gap-x-8">
186+
<Row name="Name" path="/pypi/name/:pkg" inject={['janus']} />
187+
<Row name="Version" path="/pypi/version/:pkg" inject={['janus']} />
188+
<Row name="Name and Version" path="/pypi/info/:pkg" inject={['janus']} />
189+
<Row name="License" path="/pypi/license/:pkg" inject={['janus']} />
190+
</div>
183191
</Section>
184192
</div>
185193
</main>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import PyPI from '@/utils/PyPI'
5+
6+
interface Params {
7+
params: {
8+
pkg: string
9+
}
10+
}
11+
12+
export async function GET(request: NextRequest, { params: { pkg } }: Params) {
13+
const data = await PyPI.getPackage(pkg, 'latest')
14+
if (data === null) return await Badge.error(request, 'pypi')
15+
return await Badge.generate(request, 'pypi', `${data.name} v${data.version}`)
16+
}
17+
18+
export const runtime = 'edge'
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import PyPI from '@/utils/PyPI'
5+
6+
interface Params {
7+
params: {
8+
pkg: string
9+
}
10+
}
11+
12+
export async function GET(request: NextRequest, { params: { pkg } }: Params) {
13+
const data = await PyPI.getPackage(pkg, 'latest')
14+
if (data === null) return await Badge.error(request, 'pypi')
15+
const license = data.license || 'unknown'
16+
const color = data.license ? 'blue' : 'gray'
17+
return await Badge.generate(request, 'license', `${license}`, { color })
18+
}
19+
20+
export const runtime = 'edge'
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import PyPI from '@/utils/PyPI'
5+
6+
interface Params {
7+
params: {
8+
pkg: string
9+
}
10+
}
11+
12+
export async function GET(request: NextRequest, { params: { pkg } }: Params) {
13+
const data = await PyPI.getPackage(pkg, 'latest')
14+
if (data === null) return await Badge.error(request, 'pypi')
15+
return await Badge.generate(request, 'pypi', `${data.name}`)
16+
}
17+
18+
export const runtime = 'edge'
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { NextRequest } from "next/server"
2+
3+
import Badge from '@/utils/Badge'
4+
import PyPI from '@/utils/PyPI'
5+
6+
interface Params {
7+
params: {
8+
pkg: string
9+
}
10+
}
11+
12+
export async function GET(request: NextRequest, { params: { pkg } }: Params) {
13+
const data = await PyPI.getPackage(pkg, 'latest')
14+
if (data === null) return await Badge.error(request, 'pypi')
15+
return await Badge.generate(request, 'pypi', `v${data.version}`)
16+
}
17+
18+
export const runtime = 'edge'

badgers-web/src/utils/PyPI.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Docs: https://warehouse.pypa.io/api-reference/json.html
2+
3+
const BASE_URL = 'https://pypi.org/pypi'
4+
5+
type VersionIdentifier = string | "latest"
6+
7+
type Package = {
8+
name: string
9+
version: string
10+
license: string
11+
// ...
12+
}
13+
14+
const fetchOptions = {
15+
next: {
16+
revalidate: 300, // 5m
17+
}
18+
}
19+
20+
export default class PyPI {
21+
/**
22+
* Get the package information for the given version.
23+
*
24+
* @param packageName The package name
25+
* @param version The version or `'latest'`
26+
* @returns The package information for the given version, or `null`
27+
*
28+
* @example
29+
* ```ts
30+
* await PyPI.getPackageVersion('requests', 'latest')
31+
* await PyPI.getPackageVersion('numpy', '1.24.3')
32+
* ```
33+
*/
34+
static async getPackage(packageName: string, version: VersionIdentifier): Promise<Package | null> {
35+
const url = (version === 'latest') ?
36+
`${BASE_URL}/${packageName}/json` :
37+
`${BASE_URL}/${packageName}/${version}/json`
38+
39+
try {
40+
const response = await fetch(url, fetchOptions)
41+
42+
if (response.status === 404) {
43+
return null
44+
}
45+
46+
const resp = await response.json() as { info: Package }
47+
return resp.info
48+
} catch {
49+
return null
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)