debt-atlas 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- debt_atlas-0.1.0/LICENSE +21 -0
- debt_atlas-0.1.0/PKG-INFO +102 -0
- debt_atlas-0.1.0/README.md +354 -0
- debt_atlas-0.1.0/debt_atlas/__init__.py +69 -0
- debt_atlas-0.1.0/debt_atlas/_meta.py +9 -0
- debt_atlas-0.1.0/debt_atlas/cli.py +63 -0
- debt_atlas-0.1.0/debt_atlas/client.py +318 -0
- debt_atlas-0.1.0/debt_atlas/live.py +63 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/PKG-INFO +102 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/SOURCES.txt +28 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/dependency_links.txt +1 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/entry_points.txt +2 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/requires.txt +1 -0
- debt_atlas-0.1.0/debt_atlas.egg-info/top_level.txt +1 -0
- debt_atlas-0.1.0/docs/LIBRARY.md +77 -0
- debt_atlas-0.1.0/pyproject.toml +41 -0
- debt_atlas-0.1.0/setup.cfg +4 -0
- debt_atlas-0.1.0/tests/test_atlas.py +196 -0
- debt_atlas-0.1.0/tests/test_country_profiles.py +376 -0
- debt_atlas-0.1.0/tests/test_debt_flows.py +123 -0
- debt_atlas-0.1.0/tests/test_fetcher.py +295 -0
- debt_atlas-0.1.0/tests/test_library.py +152 -0
- debt_atlas-0.1.0/tests/test_map_geometry.py +76 -0
- debt_atlas-0.1.0/tests/test_processor.py +78 -0
- debt_atlas-0.1.0/tests/test_relationship_map.py +321 -0
- debt_atlas-0.1.0/tests/test_share.py +68 -0
- debt_atlas-0.1.0/tests/test_spending_exports.py +127 -0
- debt_atlas-0.1.0/tests/test_ui_state.py +44 -0
- debt_atlas-0.1.0/tests/test_validate.py +82 -0
- debt_atlas-0.1.0/tests/test_website.py +78 -0
debt_atlas-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jaudat Faisal Al-Husein and Global Debt & Fiscal Atlas contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: debt-atlas
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sovereign debt, deficits, government spending and who-owes-whom data for every country — World Bank and IMF, with provenance.
|
|
5
|
+
Author-email: Jaudat Faisal Al-Husein <jaudatalhusein@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://jaudat150.github.io/global-debt-explorer/
|
|
8
|
+
Project-URL: Source, https://github.com/Jaudat150/global-debt-explorer
|
|
9
|
+
Project-URL: Documentation, https://github.com/Jaudat150/global-debt-explorer/blob/main/docs/LIBRARY.md
|
|
10
|
+
Project-URL: Issues, https://github.com/Jaudat150/global-debt-explorer/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/Jaudat150/global-debt-explorer/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: sovereign debt,public debt,government spending,military spending,world bank,imf,open data,public finance,economics,fiscal,bilateral debt,china lending
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: pandas>=1.5
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# debt-atlas
|
|
27
|
+
|
|
28
|
+
**Sovereign debt, deficits, government spending and who-owes-whom data for every country — in one line of Python.**
|
|
29
|
+
|
|
30
|
+
`debt-atlas` is the Python library of the open-source [Global Debt & Fiscal Atlas](https://github.com/Jaudat150/global-debt-explorer).
|
|
31
|
+
It gives you 37 indicators from the World Bank and IMF, government spending in US dollars, national debt profiles and
|
|
32
|
+
bilateral lending between countries as tidy pandas DataFrames — every value with its source, series code and year.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install debt-atlas
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import debt_atlas as da
|
|
42
|
+
|
|
43
|
+
da.latest("gov_debt_gdp", countries=["Japan", "Italy", "USA"]) # government debt, % of GDP
|
|
44
|
+
da.rank("military_usd", n=10) # biggest military spenders, US$
|
|
45
|
+
da.spending("United States") # where the money goes, in US$
|
|
46
|
+
da.who_owes("Kenya") # Kenya's lenders and how much
|
|
47
|
+
da.owed_by("China") # who owes China
|
|
48
|
+
da.series("inflation", "Argentina", start=2010) # history, one row per source
|
|
49
|
+
da.compare(["Egypt", "Pakistan", "Kenya"], ["gov_debt_gdp", "interest_gdp", "health_gov_gdp"])
|
|
50
|
+
da.profile("Syria") # 3-tier national debt profile
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Countries can be given as names (`"Kenya"`), ISO2 (`"KE"`) or ISO3 (`"KEN"`).
|
|
54
|
+
|
|
55
|
+
## What is inside
|
|
56
|
+
|
|
57
|
+
| Function | Returns |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `indicators(category=None)` | Every indicator: id, name, unit, description, upstream series |
|
|
60
|
+
| `latest(indicator, countries=None)` | Latest value per country, with year, estimate flag and source |
|
|
61
|
+
| `rank(indicator, n=10, lowest=False)` | Top or bottom countries |
|
|
62
|
+
| `series(indicator, countries, start, end, provider)` | Full history, one row per upstream series |
|
|
63
|
+
| `wide(indicator, countries)` | Year × country table |
|
|
64
|
+
| `compare(countries, indicators)` | Several indicators for several countries side by side |
|
|
65
|
+
| `spending(country=None, item=None)` | Military, health, education, social transfers, wages, interest, R&D, total — US$, per person, % of GDP |
|
|
66
|
+
| `who_owes(country)` / `owed_by(country)` | Bilateral lenders / borrowers with amounts and shares (World Bank IDS) |
|
|
67
|
+
| `bilateral(debtor, creditor, year)` | The full who-owes-whom table |
|
|
68
|
+
| `profile(country)` | National debt profile with source tier |
|
|
69
|
+
| `sources(topic=None)` | Where to find newer or more detailed data |
|
|
70
|
+
| `countries()` | ISO codes, names, coordinates |
|
|
71
|
+
| `live(series_code)` | Any series straight from the World Bank or IMF API |
|
|
72
|
+
| `download_all(folder)` | Every file plus a Frictionless `datapackage.json` |
|
|
73
|
+
|
|
74
|
+
Categories: `debt`, `spending`, `revenue`, `economy`, `flows`, `people`.
|
|
75
|
+
|
|
76
|
+
## Command line
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
debt-atlas rank military_usd -n 10
|
|
80
|
+
debt-atlas latest gov_debt_gdp --countries Japan Italy
|
|
81
|
+
debt-atlas who-owes Pakistan
|
|
82
|
+
debt-atlas spending "United States" --csv > usa.csv
|
|
83
|
+
debt-atlas download ./atlas-data
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Where the data comes from
|
|
87
|
+
|
|
88
|
+
The project rebuilds its data every week from the World Bank (World Development Indicators, International Debt
|
|
89
|
+
Statistics) and IMF (World Economic Outlook, Global Debt Database, Public Finances in Modern History) APIs, validates it
|
|
90
|
+
against published reference figures, and publishes it as CSV. The library downloads those files (cached for 24 hours
|
|
91
|
+
in `~/.cache/debt_atlas`) — fast, no API keys, and identical to what the web app shows.
|
|
92
|
+
|
|
93
|
+
- IMF values for the current year are staff estimates and are flagged `is_estimate`; projections are never included.
|
|
94
|
+
- Missing values are absent, never zero.
|
|
95
|
+
- Use another source with `da.Atlas("path/or/url")`, or set `DEBT_ATLAS_SOURCE`.
|
|
96
|
+
|
|
97
|
+
## Citing
|
|
98
|
+
|
|
99
|
+
Please cite the original data providers named in each row's `source` column, and this project
|
|
100
|
+
(see [CITATION.cff](https://github.com/Jaudat150/global-debt-explorer/blob/main/CITATION.cff)).
|
|
101
|
+
|
|
102
|
+
Code: MIT licence. Data: each provider's terms (World Bank CC BY 4.0, IMF, SIPRI, WHO, UNESCO).
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
# 🌐 Global Debt & Fiscal Atlas
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Jaudat150/global-debt-explorer/actions/workflows/tests.yml)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
[](CITATION.cff)
|
|
8
|
+
|
|
9
|
+
**Who owes what, who spends what — for every country, with the source and year of every number.**
|
|
10
|
+
|
|
11
|
+
An open-source atlas of sovereign debt, deficits, government spending, revenue, the economy and development, built
|
|
12
|
+
for students, researchers, journalists and anyone curious about public money. Data comes **live** from the World Bank
|
|
13
|
+
and IMF APIs; when a number is missing or out of date, the app tells you exactly where to find a better one.
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<b>🗺️ Interactive app</b> · <a href="https://jaudat150.github.io/global-debt-explorer/"><b>🌐 Website: every country</b></a> ·
|
|
17
|
+
<a href="docs/LIBRARY.md"><b>🐍 <code>pip install debt-atlas</code></b></a> · <a href="https://jaudat150.github.io/global-debt-explorer/data/"><b>⬇️ Data</b></a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
## Why this project
|
|
21
|
+
|
|
22
|
+
Public-finance data is scattered across a dozen institutions, each with its own codes, units, definitions and
|
|
23
|
+
release calendars. This atlas puts 37 indicators on one map while keeping what researchers need:
|
|
24
|
+
|
|
25
|
+
- **Provenance on every value** — source, series code, year, and whether it is an IMF estimate.
|
|
26
|
+
- **Freshness first** — when two series share a definition, the most recent valid value per country wins.
|
|
27
|
+
Projections are never shown as data.
|
|
28
|
+
- **Honest gaps** — missing is never zero. Grey countries link to 31 curated sources for newer or
|
|
29
|
+
more detailed data.
|
|
30
|
+
- **Works offline** — 24-hour caching, a weekly snapshot refreshed by GitHub Actions, and graceful fallback when an
|
|
31
|
+
API is down.
|
|
32
|
+
|
|
33
|
+
## What you can do
|
|
34
|
+
|
|
35
|
+
| View | What it shows |
|
|
36
|
+
|---|---|
|
|
37
|
+
| 🗺️ **World atlas** | Choropleth map of any indicator; click a country to draw **who-owes-whom arrows** (lender → borrower, with amounts); top/bottom rankings; full table and CSV export with provenance; "find newer data" links. |
|
|
38
|
+
| 📊 **Country profile** | Headline numbers, **where the money goes** (defence, health, education, interest, R&D as % of GDP), a 25-year trend for any indicator, and links to the IMF, World Bank, budget documents and national statistics office. |
|
|
39
|
+
| 💸 **Spending** | What each government spends on defence, health, education, social transfers, wages, interest and R&D — in US$, per person and % of GDP, with the exact series behind every number — plus a world ranking for each line. |
|
|
40
|
+
| 🔗 **Who owes whom** | Bilateral lending from World Bank International Debt Statistics: arrows from every lender to the selected borrower **and** from the selected lender to everyone who owes it, with amounts, shares and each country's national debt profile. |
|
|
41
|
+
| 📚 **Data & sources** | Live freshness check of every indicator, one-click download of the full dataset, and the full source directory. |
|
|
42
|
+
| 🧑💻 **Developers** | One-click ZIP of all data with a Frictionless `datapackage.json`, every upstream API with live links and code examples (curl, Python, R), the project's Python API and CLI, and a full data dictionary. |
|
|
43
|
+
|
|
44
|
+
### Share any view
|
|
45
|
+
|
|
46
|
+
Every view has its own link, and the **🔗 Share** button copies it:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
?view=atlas&indicator=military_gdp&country=KEN military spending map, Kenya selected
|
|
50
|
+
?view=spending&country=USA&item=health where the US spends, health ranking
|
|
51
|
+
?view=debt&country=CHN who owes China, and whom China owes
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Indicators
|
|
55
|
+
|
|
56
|
+
| Category | # | Indicators |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| 🏦 **Debt & deficits** | 7 | Gov. debt, Fiscal balance, External debt, External debt / GNI, Debt service / exports, Interest / revenue, Private debt |
|
|
59
|
+
| 🧾 **Government spending** | 11 | Total spending, Military, Military / budget, Health, Health / budget, Education, Education / budget, Interest, R&D, Public wages, Subsidies & transfers |
|
|
60
|
+
| 💰 **Revenue & taxes** | 2 | Revenue, Tax revenue |
|
|
61
|
+
| 📈 **Economy** | 6 | GDP, GDP per capita, GDP growth, Inflation, Unemployment, Current account |
|
|
62
|
+
| 🌍 **External finance** | 4 | FDI inflows, Remittances, Aid received, Reserves cover |
|
|
63
|
+
| 👥 **People & development** | 4 | Population, Life expectancy, Extreme poverty, Inequality (Gini) |
|
|
64
|
+
|
|
65
|
+
Full definitions, series codes and links: **[docs/DATA_SOURCES.md](docs/DATA_SOURCES.md)**.
|
|
66
|
+
|
|
67
|
+
## Python library — `pip install debt-atlas`
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import debt_atlas as da
|
|
71
|
+
|
|
72
|
+
da.rank("military_usd", n=10) # biggest military spenders, US$
|
|
73
|
+
da.latest("gov_debt_gdp", countries=["Japan", "Italy", "USA"])
|
|
74
|
+
da.spending("United States") # where the money goes, in US$
|
|
75
|
+
da.who_owes("Kenya") # Kenya's lenders and how much
|
|
76
|
+
da.owed_by("China") # who owes China
|
|
77
|
+
da.series("inflation", "Argentina", start=2010) # full history, per source
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Lightweight (pandas only), cached, no API keys; accepts country names, ISO2 or ISO3. Also a command line:
|
|
81
|
+
`debt-atlas who-owes Pakistan`. Full reference: **[docs/LIBRARY.md](docs/LIBRARY.md)**.
|
|
82
|
+
|
|
83
|
+
## Website — a page for every country
|
|
84
|
+
|
|
85
|
+
[jaudat150.github.io/global-debt-explorer](https://jaudat150.github.io/global-debt-explorer/) has a fast, readable page for
|
|
86
|
+
every country (debt, spending, who it owes, who owes it, FAQ) and every indicator (full ranking with sources), rebuilt
|
|
87
|
+
weekly from the same validated data (`python -m src.website build`).
|
|
88
|
+
|
|
89
|
+
## Quick start
|
|
90
|
+
|
|
91
|
+
Python 3.11–3.13.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
git clone https://github.com/Jaudat150/global-debt-explorer.git
|
|
95
|
+
cd global-debt-explorer
|
|
96
|
+
python -m venv .venv
|
|
97
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
98
|
+
pip install -r requirements.txt
|
|
99
|
+
streamlit run app.py
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Open <http://localhost:8501>. The atlas, country profiles and sources work immediately. The **Who owes whom** view
|
|
103
|
+
needs the bilateral cache (`data/bilateral_debt_cache.json`, included; refresh with `python -m src.fetcher`).
|
|
104
|
+
Windows users: see [START_HERE_WINDOWS.txt](START_HERE_WINDOWS.txt).
|
|
105
|
+
|
|
106
|
+
### Deploy your own copy for free
|
|
107
|
+
|
|
108
|
+
1. Fork this repository.
|
|
109
|
+
2. On [Streamlit Community Cloud](https://streamlit.io/cloud), choose **New app**, select your fork and `app.py`.
|
|
110
|
+
3. Enable GitHub Actions on your fork so the weekly data snapshot keeps refreshing.
|
|
111
|
+
|
|
112
|
+
## How fresh is the data?
|
|
113
|
+
|
|
114
|
+
| Layer | Source | Refresh |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| Atlas indicators | World Bank WDI/IDS API, IMF DataMapper API | Live, cached 24 h; weekly snapshot via `refresh_indicators.yml` |
|
|
117
|
+
| National debt profile | World Bank → IMF → curated estimates → last-known snapshot | Live, cached 24 h |
|
|
118
|
+
| Bilateral debt | World Bank IDS (source 6) | Monthly via `update_data.yml` |
|
|
119
|
+
|
|
120
|
+
The IMF publishes the World Economic Outlook in April and October; values for the current year are IMF staff
|
|
121
|
+
estimates and are flagged (toggle **Reported data only** to hide them). The World Bank publishes WDI several times a
|
|
122
|
+
year and International Debt Statistics each December. When the live sources lag, the in-app **Find newer data**
|
|
123
|
+
panel and [docs/DATA_SOURCES.md](docs/DATA_SOURCES.md) list where newer figures appear first — usually national
|
|
124
|
+
statistics offices, ministries of finance, SIPRI, WHO and the IMF Fiscal Monitor.
|
|
125
|
+
|
|
126
|
+
## Open data for everyone
|
|
127
|
+
|
|
128
|
+
Every dataset is published as plain CSV with a [Frictionless Data Package](https://specs.frictionlessdata.io/data-package/)
|
|
129
|
+
descriptor, rebuilt weekly by GitHub Actions into [`data/exports/`](data/exports/README.md):
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import pandas as pd
|
|
133
|
+
base = "https://raw.githubusercontent.com/Jaudat150/global-debt-explorer/main/data/exports"
|
|
134
|
+
latest = pd.read_csv(f"{base}/latest_values.csv") # every indicator, every country, with provenance
|
|
135
|
+
spending = pd.read_csv(f"{base}/spending_usd.csv") # where the money goes, in US$
|
|
136
|
+
history = pd.read_csv(f"{base}/timeseries.csv.gz") # full per-source time series
|
|
137
|
+
owes = pd.read_csv(f"{base}/bilateral_debt.csv.gz") # who owes whom
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Or build everything locally: `python -m src.exports build`, or use **Developers → Build the full data bundle** in the app.
|
|
141
|
+
|
|
142
|
+
## Using the data in research and teaching
|
|
143
|
+
|
|
144
|
+
- **CSV exports** from the atlas and the Data & sources view include ISO3, value, unit, year, estimate flag, source,
|
|
145
|
+
series code, series URL and retrieval timestamp.
|
|
146
|
+
- **Command line:**
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
python -m src.indicator_data show gov_debt_gdp KEN GHA ZMB # one indicator, any countries
|
|
150
|
+
python -m src.indicator_data refresh # download everything, update the snapshot
|
|
151
|
+
python -m src.processor profiles SYR PSE CAN SAU # national debt profiles with tier trail
|
|
152
|
+
python -m src.catalog # list every indicator and series code
|
|
153
|
+
python -m src.exports build # every dataset as CSV + datapackage.json
|
|
154
|
+
python -m src.validate # check coverage, reference figures, consistency
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- **Cite** this project via [CITATION.cff](CITATION.cff) (GitHub shows a "Cite this repository" button) **and** the
|
|
158
|
+
original data providers shown next to each value.
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
161
|
+
|
|
162
|
+
```mermaid
|
|
163
|
+
flowchart LR
|
|
164
|
+
subgraph Live["Live APIs (24 h cache)"]
|
|
165
|
+
WB[World Bank V2<br/>WDI · IDS]
|
|
166
|
+
IMF[IMF DataMapper<br/>WEO · GDD · FPP]
|
|
167
|
+
end
|
|
168
|
+
CAT[src/catalog.py<br/>indicators + source directory] --> DATA
|
|
169
|
+
WB --> DATA[src/indicator_data.py<br/>freshest-wins resolver]
|
|
170
|
+
IMF --> DATA
|
|
171
|
+
SNAP[(data/indicator_snapshot.json<br/>weekly via Actions)] -. source down .-> DATA
|
|
172
|
+
DATA --> ATLAS[src/atlas_map.py<br/>choropleth]
|
|
173
|
+
DATA --> VIEWS[src/atlas_view.py<br/>atlas · country · sources]
|
|
174
|
+
WB --> PROF[src/processor.py<br/>3-tier national debt profile]
|
|
175
|
+
IMF --> PROF
|
|
176
|
+
IDS[(data/bilateral_debt_cache.json<br/>monthly)] --> REL[src/relationship_map.py]
|
|
177
|
+
PROF --> REL
|
|
178
|
+
ATLAS --> APP[app.py · Streamlit]
|
|
179
|
+
VIEWS --> APP
|
|
180
|
+
REL --> APP
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Repository layout
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
global-debt-explorer/
|
|
187
|
+
├── app.py # Streamlit entry point and view switcher
|
|
188
|
+
├── debt_atlas/ # Python library (pip install debt-atlas)
|
|
189
|
+
├── pyproject.toml # library packaging
|
|
190
|
+
├── src/
|
|
191
|
+
│ ├── catalog.py # indicator catalogue + external source directory (pure data)
|
|
192
|
+
│ ├── indicator_data.py # live download, 24 h cache, freshest-wins resolution, snapshot, CLI
|
|
193
|
+
│ ├── atlas_map.py # choropleth + legend
|
|
194
|
+
│ ├── atlas_view.py # World atlas, Country profile, Data & sources views
|
|
195
|
+
│ ├── debt_flows.py # who-owes-whom: lender → borrower arrows and tables
|
|
196
|
+
│ ├── spending.py / spending_view.py # where the money goes, in US$
|
|
197
|
+
│ ├── exports.py / developer_view.py # open-data exports, datapackage.json, Developers tab
|
|
198
|
+
│ ├── website.py # static SEO website (country + indicator pages, sitemap)
|
|
199
|
+
│ ├── validate.py · share.py # data validation · shareable links
|
|
200
|
+
│ ├── fetcher.py # World Bank IDS bilateral pipeline + national-profile API clients
|
|
201
|
+
│ ├── processor.py # IDS processing + 3-tier national debt profiles
|
|
202
|
+
│ ├── relationship_map.py # Who-owes-whom map
|
|
203
|
+
│ ├── map_geometry.py # Natural Earth boundaries
|
|
204
|
+
│ └── ui_state.py
|
|
205
|
+
├── data/ # caches, snapshots, label points (see data/README.md)
|
|
206
|
+
├── docs/DATA_SOURCES.md # generated from src/catalog.py
|
|
207
|
+
├── tests/ # network-free unit tests
|
|
208
|
+
├── .github/ # CI, weekly/monthly data refresh, issue & PR templates
|
|
209
|
+
├── CITATION.cff · CONTRIBUTING.md · CODE_OF_CONDUCT.md · ROADMAP.md · CHANGELOG.md · LICENSE
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Methodology details
|
|
213
|
+
|
|
214
|
+
### National debt profile: 3-tier fallback
|
|
215
|
+
|
|
216
|
+
The national-profile layer is independent of the bilateral IDS cache. For **every** ISO-3 code, `src/processor.py` resolves a profile in this order and never raises because of a network failure:
|
|
217
|
+
|
|
218
|
+
| Tier | Source | Indicator | Returned metric |
|
|
219
|
+
|---|---|---|---|
|
|
220
|
+
| 1 — Primary | World Bank V2 API | `DT.DOD.DLXF.CD` | Long-term external debt stocks, current US$ |
|
|
221
|
+
| 2 — Secondary | IMF DataMapper API | `GGXWDG_NGDP` (World Economic Outlook), then `GG_DEBT_GDP` (Global Debt Database) | General government gross debt, % of GDP |
|
|
222
|
+
| 3 — Safety net | a) `LOCAL_FALLBACK_DB` · b) last-known-good snapshot | Curated, sourced estimates · last value retrieved from a live source | The metric stated by the record |
|
|
223
|
+
| — | none of the above | — | Explicit `No verified data` profile (never zero), still plotted |
|
|
224
|
+
|
|
225
|
+
A tier is skipped — and the next one tried — when its source is **unreachable**, has **no non-null value**, returns **zero/negative/non-numeric** data, or its latest observation is **older than 10 years** (`GDE_MAX_OBSERVATION_AGE_YEARS`). Each profile carries a `resolution_trail`, e.g. `Tier 1 World Bank: no non-null observation → Tier 2 IMF: latest non-null observation used`, which the dashboard shows under the country panel.
|
|
226
|
+
|
|
227
|
+
#### Tier 1 — World Bank
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
https://api.worldbank.org/v2/country/{ISO3}/indicator/DT.DOD.DLXF.CD?format=json
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The parser reads the observation array at index `[1]` and selects the latest year with a non-null value. For the map, one paginated `country/all` request replaces ~250 single-country calls; if that bulk request fails, the exact per-country route above is queried concurrently behind a circuit breaker (it stops after 5 consecutive failures so an outage costs seconds, not minutes).
|
|
234
|
+
|
|
235
|
+
**Definition:** `DT.DOD.DLXF.CD` is long-term external debt — public, publicly guaranteed **and** private nonguaranteed — not public debt alone. High-income creditor countries such as Canada and Saudi Arabia do not report to IDS, so they normally resolve at Tier 2. See the [World Bank debt-statistics FAQ](https://www.worldbank.org/en/programs/debt-statistics/faq).
|
|
236
|
+
|
|
237
|
+
#### Tier 2 — IMF DataMapper
|
|
238
|
+
|
|
239
|
+
```text
|
|
240
|
+
https://www.imf.org/external/datamapper/api/v1/GG_DEBT_GDP
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The full payload is downloaded once and read at `data["values"]["GG_DEBT_GDP"][code]`.
|
|
244
|
+
|
|
245
|
+
- **Projections excluded.** DataMapper series include World Economic Outlook forecasts up to five years ahead. Years after the current year are ignored, so a 2030 forecast is never shown as the latest value. Current-year values are flagged as IMF staff estimates.
|
|
246
|
+
- **Code aliases.** The IMF uses `WBG` for West Bank and Gaza and `UVK` for Kosovo; these are translated automatically.
|
|
247
|
+
- This is a **ratio**, displayed as `% of GDP`, and is never ranked against US$ stocks. See the [IMF DataMapper API](https://www.imf.org/external/datamapper/api/).
|
|
248
|
+
|
|
249
|
+
#### Tier 3 — local safety net
|
|
250
|
+
|
|
251
|
+
**a) Curated estimates — `LOCAL_FALLBACK_DB`.** Used when both live tiers come up empty:
|
|
252
|
+
|
|
253
|
+
- **Syria (`SYR`):** US$20–23B estimated total external debt (range attributed to the post-2024 government), reported by [PIIE](https://www.piie.com/blogs/realtime-economics/2025/syria-needs-debt-restructuring-will-be-difficult) and the [EUAA](https://www.euaa.europa.eu/coi/syria/2025/country-focus/3-socio-economic-situation-focus-damascus-city/31-economic-situation-overview). PIIE describes the World Bank's figure (~US$4.1B, 2023) as very incomplete.
|
|
254
|
+
- **West Bank and Gaza (`PSE`):** US$4.8B Palestinian Authority public debt at end-2025, [World Bank Palestinian Economic Monitor](https://thedocs.worldbank.org/en/doc/caf69bb9db6d729c6b35254c5815df9a-0280012026/original/Palestinian-Economic-Monitor-5-19-26web.pdf) (May 19, 2026). Excludes large arrears.
|
|
255
|
+
|
|
256
|
+
Each record keeps its value or range, metric type, observation year, source URL, source publication date, review date (`reviewed_at`) and a perimeter caveat. When a live tier *does* return a value for one of these countries, the curated figure is not discarded: it is attached as `alternative_estimate` and shown as "Other estimate". Set `"prefer_over_live": True` on an entry only if you have a documented reason to rank it above the live series.
|
|
257
|
+
|
|
258
|
+
To add a country, append a record with the same fields. Add one only when a credible source, value or range, period and source date can be recorded.
|
|
259
|
+
|
|
260
|
+
**b) Last-known-good snapshot.** Every successful live response is merged into `data/country_profiles_snapshot.json`. If the World Bank or IMF is **unreachable** (not merely "no data"), the last known value is used, labelled `Last retrieved YYYY-MM-DD · live source unreachable`. The monthly GitHub workflow refreshes this file; you can also run `python -m src.processor profiles`.
|
|
261
|
+
|
|
262
|
+
#### Stable country-profile contract
|
|
263
|
+
|
|
264
|
+
Every valid ISO-3 code returns a profile with at least:
|
|
265
|
+
|
|
266
|
+
- `iso3`, `country_name`
|
|
267
|
+
- `value`, `value_low`, `value_high`, `display_value`
|
|
268
|
+
- `metric_type`, `metric_label`, `metric_short`, `unit`, `observation_year`
|
|
269
|
+
- `source_label`, `data_source`, `source_url`, `source_timestamp`, `source_date_label`
|
|
270
|
+
- `fallback_tier`, `tier_label`, `is_estimate`, `is_stale_snapshot`, `data_available`
|
|
271
|
+
- `coordinates` (`{"latitude", "longitude"}`), `map_available`, `definition_note`
|
|
272
|
+
- `resolution_trail`, `alternative_estimate`
|
|
273
|
+
|
|
274
|
+
Coordinates come from `data/country_label_points.json` (Natural Earth 1:10m label points, 241 economies, including small states absent from the 1:110m outlines). The map prefers a polygon's representative point where one exists. Natural Earth's own codes for Palestine (`PSX`), South Sudan (`SDS`), Kosovo (`KOS`) and Western Sahara (`SAH`) are translated to ISO codes so those countries match their data.
|
|
275
|
+
|
|
276
|
+
#### Caching, outages and refresh
|
|
277
|
+
|
|
278
|
+
- Live API responses are cached with `@st.cache_data(ttl=86400)` — 24 hours ([docs](https://docs.streamlit.io/develop/api-reference/caching-and-state/st.cache_data)).
|
|
279
|
+
- **Failures are never cached:** a failed call raises inside the cached function, so Streamlit stores nothing. A 5-minute in-process back-off then stops the dead source from adding timeout latency to every rerun.
|
|
280
|
+
- If both live APIs are down, the dashboard shows a banner and runs on Tier 3.
|
|
281
|
+
- **Refresh country data** clears the 24-hour cache and the back-off.
|
|
282
|
+
- Request timeouts: 5 s connect / 20 s read, up to 3 attempts with exponential back-off honouring `Retry-After`.
|
|
283
|
+
|
|
284
|
+
#### Debugging a country from the command line
|
|
285
|
+
|
|
286
|
+
```cmd
|
|
287
|
+
python -m src.processor profiles CAN SAU SYR PSE
|
|
288
|
+
python -m src.processor profiles SYR --json
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Prints each country's tier, value and source; `--json` includes the full resolution trail.
|
|
292
|
+
|
|
293
|
+
### Bilateral IDS data (Who owes whom)
|
|
294
|
+
|
|
295
|
+
The bilateral relationship map continues to use World Bank International Debt Statistics (IDS), source ID `6`, indicator `DT.DOD.BLAT.CD` — *PPG, bilateral (DOD, current US$)*.
|
|
296
|
+
|
|
297
|
+
| Field | Definition |
|
|
298
|
+
|---|---|
|
|
299
|
+
| Debtor | Reporting economy in the IDS debtor dimension |
|
|
300
|
+
| Creditor | IDS `counterpart-area` identifier and label |
|
|
301
|
+
| Amount | Public and publicly guaranteed bilateral debt outstanding and disbursed, current US dollars |
|
|
302
|
+
| Time | Annual observation year |
|
|
303
|
+
|
|
304
|
+
`src/fetcher.py` retrieves counterpart-area metadata, sends semicolon-delimited creditor ID batches, follows pagination, retries transient errors, and recursively splits batches rejected by the API. It requests `time/all` then filters the requested year range locally. The default start year is 2000; use `--start-year 1960` for a longer history.
|
|
305
|
+
|
|
306
|
+
The monthly GitHub Actions workflow refreshes this bilateral cache and the country-profile snapshot. National country profiles are requested by the dashboard and cached for 24 hours at runtime.
|
|
307
|
+
|
|
308
|
+
### Map tiles
|
|
309
|
+
|
|
310
|
+
The default basemap uses OpenStreetMap Standard tiles and keeps the required attribution visible. Follow the [OpenStreetMap tile usage policy](https://operations.osmfoundation.org/policies/tiles/); the public tile service is best-effort and is not intended for heavy production traffic. For public deployments with more traffic, use a dedicated tile provider or host your own tiles.
|
|
311
|
+
|
|
312
|
+
Optional CARTO Positron support is available with a user-provided API key. Never commit keys:
|
|
313
|
+
|
|
314
|
+
```cmd
|
|
315
|
+
set GDE_MAP_BASEMAP=carto
|
|
316
|
+
set CARTO_BASEMAP_API_KEY=YOUR_PERSONAL_KEY
|
|
317
|
+
python -m streamlit run app.py
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Without a key, the app automatically uses OpenStreetMap.
|
|
321
|
+
|
|
322
|
+
## Tests
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
python -m unittest discover -s tests -v
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
The suite is network-free (all HTTP is mocked) and covers API parsing, projection and estimate handling,
|
|
329
|
+
freshest-wins resolution, outage back-off, snapshots, colour classification, maps, the 3-tier national profile and
|
|
330
|
+
the bilateral pipeline. CI runs it on Python 3.11, 3.12 and 3.13 and checks that `docs/DATA_SOURCES.md` matches the
|
|
331
|
+
catalogue.
|
|
332
|
+
|
|
333
|
+
## Team
|
|
334
|
+
|
|
335
|
+
Created and led by **Jaudat Faisal Al-Husein** ([@Jaudat150](https://github.com/Jaudat150)). Maintainers, roles and how
|
|
336
|
+
the team works: [docs/TEAM_PLAYBOOK.md](docs/TEAM_PLAYBOOK.md).
|
|
337
|
+
|
|
338
|
+
## Contributing
|
|
339
|
+
|
|
340
|
+
Contributions of every size are welcome — reporting a stale number is as valuable as code. Adding an indicator is
|
|
341
|
+
usually about ten lines in `src/catalog.py`. See [CONTRIBUTING.md](CONTRIBUTING.md) and [ROADMAP.md](ROADMAP.md).
|
|
342
|
+
|
|
343
|
+
## Licence and data attribution
|
|
344
|
+
|
|
345
|
+
Code: [MIT](LICENSE). Data remain subject to their providers' terms:
|
|
346
|
+
[World Bank](https://www.worldbank.org/en/about/legal/terms-of-use-for-datasets) (CC BY 4.0),
|
|
347
|
+
[IMF](https://www.imf.org/external/terms.htm), SIPRI, WHO, UNESCO and the other sources listed with each value.
|
|
348
|
+
Boundaries: [Natural Earth](https://www.naturalearthdata.com/) (public domain). Map tiles:
|
|
349
|
+
[© OpenStreetMap contributors](https://www.openstreetmap.org/copyright).
|
|
350
|
+
|
|
351
|
+
## Author
|
|
352
|
+
|
|
353
|
+
Created and maintained by **Eng. Jaudat Faisal Al-Husein** · [github.com/Jaudat150](https://github.com/Jaudat150) ·
|
|
354
|
+
jaudatalhusein@gmail.com
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""debt-atlas — sovereign debt, deficits, government spending and who-owes-whom data for every country.
|
|
2
|
+
|
|
3
|
+
Quick start::
|
|
4
|
+
|
|
5
|
+
import debt_atlas as da
|
|
6
|
+
|
|
7
|
+
da.indicators() # what is available
|
|
8
|
+
da.latest("gov_debt_gdp", countries=["Japan", "Italy"])
|
|
9
|
+
da.rank("military_usd", n=10) # biggest military spenders
|
|
10
|
+
da.series("inflation", "Argentina", start=2010) # history, per source
|
|
11
|
+
da.spending("United States") # where the money goes, in US$
|
|
12
|
+
da.who_owes("Kenya") # Kenya's lenders
|
|
13
|
+
da.owed_by("China") # who owes China
|
|
14
|
+
da.profile("Syria") # 3-tier national debt profile
|
|
15
|
+
da.download_all("atlas-data") # every file + datapackage.json
|
|
16
|
+
|
|
17
|
+
Every value carries its source, series code and year. Cite the original
|
|
18
|
+
providers and this project (see CITATION.cff).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
from debt_atlas._meta import REPO_URL, SITE_URL, __version__
|
|
26
|
+
from debt_atlas.client import Atlas, AtlasError
|
|
27
|
+
|
|
28
|
+
_default: Atlas | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def default() -> Atlas:
|
|
32
|
+
"""The shared client used by the module-level functions."""
|
|
33
|
+
global _default
|
|
34
|
+
if _default is None:
|
|
35
|
+
_default = Atlas()
|
|
36
|
+
return _default
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _delegate(name: str):
|
|
40
|
+
def call(*args: Any, **kwargs: Any) -> Any:
|
|
41
|
+
return getattr(default(), name)(*args, **kwargs)
|
|
42
|
+
|
|
43
|
+
call.__name__ = name
|
|
44
|
+
call.__doc__ = getattr(Atlas, name).__doc__
|
|
45
|
+
return call
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
indicators = _delegate("indicators")
|
|
49
|
+
sources = _delegate("sources")
|
|
50
|
+
countries = _delegate("countries")
|
|
51
|
+
latest = _delegate("latest")
|
|
52
|
+
rank = _delegate("rank")
|
|
53
|
+
series = _delegate("series")
|
|
54
|
+
wide = _delegate("wide")
|
|
55
|
+
compare = _delegate("compare")
|
|
56
|
+
spending = _delegate("spending")
|
|
57
|
+
profile = _delegate("profile")
|
|
58
|
+
bilateral = _delegate("bilateral")
|
|
59
|
+
who_owes = _delegate("who_owes")
|
|
60
|
+
owed_by = _delegate("owed_by")
|
|
61
|
+
download_all = _delegate("download_all")
|
|
62
|
+
live = _delegate("live")
|
|
63
|
+
resolve_country = _delegate("resolve_country")
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"Atlas", "AtlasError", "__version__", "REPO_URL", "SITE_URL", "default",
|
|
67
|
+
"indicators", "sources", "countries", "latest", "rank", "series", "wide", "compare", "spending",
|
|
68
|
+
"profile", "bilateral", "who_owes", "owed_by", "download_all", "live", "resolve_country",
|
|
69
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Project-wide constants. Change REPO_SLUG once if the GitHub repository is renamed."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
|
|
5
|
+
REPO_SLUG = "Jaudat150/global-debt-explorer"
|
|
6
|
+
BRANCH = "main"
|
|
7
|
+
RAW_BASE = f"https://raw.githubusercontent.com/{REPO_SLUG}/{BRANCH}/data/exports"
|
|
8
|
+
REPO_URL = f"https://github.com/{REPO_SLUG}"
|
|
9
|
+
SITE_URL = f"https://{REPO_SLUG.split('/')[0].lower()}.github.io/{REPO_SLUG.split('/')[1]}/"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Command line: ``debt-atlas latest gov_debt_gdp --countries Japan USA``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from debt_atlas import Atlas, AtlasError, __version__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(argv: list[str] | None = None) -> int:
|
|
14
|
+
parser = argparse.ArgumentParser(prog="debt-atlas", description="Global Debt & Fiscal Atlas data from the command line.")
|
|
15
|
+
parser.add_argument("--version", action="version", version=f"debt-atlas {__version__}")
|
|
16
|
+
parser.add_argument("--source", help="Base URL or folder of the export files")
|
|
17
|
+
parser.add_argument("--csv", action="store_true", help="Print CSV instead of a table")
|
|
18
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
19
|
+
sub.add_parser("indicators", help="List indicators")
|
|
20
|
+
p = sub.add_parser("latest", help="Latest value per country"); p.add_argument("indicator"); p.add_argument("--countries", nargs="*")
|
|
21
|
+
p = sub.add_parser("rank", help="Top countries"); p.add_argument("indicator"); p.add_argument("-n", type=int, default=10); p.add_argument("--lowest", action="store_true")
|
|
22
|
+
p = sub.add_parser("series", help="History"); p.add_argument("indicator"); p.add_argument("countries", nargs="+"); p.add_argument("--start", type=int)
|
|
23
|
+
p = sub.add_parser("spending", help="Where the money goes (US$)"); p.add_argument("country")
|
|
24
|
+
p = sub.add_parser("who-owes", help="A country's lenders"); p.add_argument("country")
|
|
25
|
+
p = sub.add_parser("owed-by", help="Who owes a country"); p.add_argument("country")
|
|
26
|
+
p = sub.add_parser("profile", help="National debt profile"); p.add_argument("country")
|
|
27
|
+
p = sub.add_parser("download", help="Save every file"); p.add_argument("folder")
|
|
28
|
+
args = parser.parse_args(argv)
|
|
29
|
+
|
|
30
|
+
atlas = Atlas(args.source) if args.source else Atlas()
|
|
31
|
+
try:
|
|
32
|
+
if args.command == "indicators":
|
|
33
|
+
result = atlas.indicators()[["id", "category", "unit", "name"]]
|
|
34
|
+
elif args.command == "latest":
|
|
35
|
+
result = atlas.latest(args.indicator, args.countries)[["iso3", "country", "value", "year", "is_estimate", "source"]]
|
|
36
|
+
elif args.command == "rank":
|
|
37
|
+
result = atlas.rank(args.indicator, args.n, lowest=args.lowest)[["iso3", "country", "value", "year", "source"]]
|
|
38
|
+
elif args.command == "series":
|
|
39
|
+
result = atlas.series(args.indicator, args.countries, start=args.start)[["iso3", "year", "value", "series_code"]]
|
|
40
|
+
elif args.command == "spending":
|
|
41
|
+
result = atlas.spending(args.country)[["item", "usd", "pct_gdp", "usd_per_person", "year", "scope"]]
|
|
42
|
+
elif args.command == "who-owes":
|
|
43
|
+
result = atlas.who_owes(args.country)
|
|
44
|
+
elif args.command == "owed-by":
|
|
45
|
+
result = atlas.owed_by(args.country)
|
|
46
|
+
elif args.command == "profile":
|
|
47
|
+
result = pd.Series(atlas.profile(args.country)).to_frame("value")
|
|
48
|
+
else:
|
|
49
|
+
print(f"Saved to {atlas.download_all(args.folder)}")
|
|
50
|
+
return 0
|
|
51
|
+
except AtlasError as exc:
|
|
52
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
53
|
+
return 2
|
|
54
|
+
if args.csv:
|
|
55
|
+
result.to_csv(sys.stdout, index=False)
|
|
56
|
+
else:
|
|
57
|
+
with pd.option_context("display.max_rows", 300, "display.width", 160, "display.max_colwidth", 60):
|
|
58
|
+
print(result.to_string(index=args.command == "profile"))
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
raise SystemExit(main())
|