diff --git a/.gitignore b/.gitignore
index 72b1f6a..3092ec3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,5 +23,11 @@ htmlcov/
.DS_Store
Thumbs.db
-# Keep egg-info for package metadata
-!global_macro_data.egg-info/
\ No newline at end of file
+# Remove egg-info
+global_macro_data.egg-info/
+
+# Test file
+test.py
+
+# Testing: .tox
+.tox/
\ No newline at end of file
diff --git a/README.md b/README.md
index ba79017..4730a09 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ This repository complements paper, **Müller, Xu, Lehbib, and Chen (2025)**, whi
- **Scheduled Updates**: Regular releases ensure data reliability.
- **Full Transparency**: All code is open source and available in this repository.
- **Accessible Formats**: Provided in `.dta`, `.csv` and as **Stata
-/Python/R package**.
+/Python/R packages**.
## Data access
@@ -30,47 +30,52 @@ pip install global_macro_data
**How to use (examples)**
```python
-from global_macro_data import gmd
+import global_macro_data as gmd
# Get data from latest available version
-df = gmd()
+df = gmd.get_data()
# Get data from a specific version
-df = gmd(version="2025_01")
+df = gmd.get_data(version="2025_01")
# Get data for a specific country
-df = gmd(country="USA")
+df = gmd.get_data(country="USA")
# Get data for multiple countries
-df = gmd(country=["USA", "CHN", "DEU"])
+df = gmd.get_data(country=["USA", "CHN", "Germany"])
# Get specific variables
-df = gmd(variables=["rGDP", "infl", "unemp"])
+df = gmd.get_data(variables=["rGDP", "infl", "unemp"])
# Get raw data for a single variable
-df = gmd(variables="rGDP", raw=True)
+df = gmd.get_data(variables="rGDP", raw=True)
# List available variables and their descriptions
-gmd(vars=True)
+gmd.list_variables()
# List available countries and their ISO codes
-gmd(iso=True)
+gmd.list_countries()
+
+# List available versions
+gmd.list_versions()
# Combine parameters
-df = gmd(
+df = gmd.get_data(
version="2025_01",
country=["USA", "CHN"],
variables=["rGDP", "unemp", "CPI"]
)
+
+# Enable verbose logging (INFO-level and above)
+gmd.enable_verbose_logging()
```
-## Parameters
+## Parameters for `gmd.get_data()`
- **variables (str or list)**: Variable code(s) to include (e.g., "rGDP" or ["rGDP", "unemp"])
-- **country (str or list)**: ISO3 country code(s) (e.g., "SGP" or ["MRT", "SGP"])
+- **country (str or list)**: Country name or ISO3 country code(s) (e.g., "SGP" or ["MRT", "SGP"])
- **version (str)**: Dataset version in format 'YYYY_MM' (e.g., '2025_01'). If None or "current", uses the latest version
- **raw (bool)**: If True, download raw data for a single variable
-- **iso (bool)**: If True, display list of available countries
-- **vars (bool)**: If True, display list of available variables
+
## Release schedule
| Release Date | Details |
diff --git a/global_macro_data.egg-info/PKG-INFO b/global_macro_data.egg-info/PKG-INFO
deleted file mode 100644
index 5d877a2..0000000
--- a/global_macro_data.egg-info/PKG-INFO
+++ /dev/null
@@ -1,123 +0,0 @@
-Metadata-Version: 2.4
-Name: global-macro-data
-Version: 0.3.1
-Summary: Global Macro Database by Karsten Müller, Chenzi Xu, Mohamed Lehbib and Ziliang Chen (2025)
-Home-page: https://github.com/KMueller-Lab/Global-Macro-Database-Python
-Author: Yangbo Wang
-Author-email: wangyangbo@ruc.edu.cn
-Classifier: Programming Language :: Python :: 3
-Classifier: License :: OSI Approved :: MIT License
-Classifier: Operating System :: OS Independent
-Requires-Python: >=3.6
-Description-Content-Type: text/markdown
-Requires-Dist: requests
-Requires-Dist: pandas
-Dynamic: author
-Dynamic: author-email
-Dynamic: classifier
-Dynamic: description
-Dynamic: description-content-type
-Dynamic: home-page
-Dynamic: requires-dist
-Dynamic: requires-python
-Dynamic: summary
-
-# The Global Macro Database (Python Package)
-
-
-
-
-[](LICENSE)
-
-[Link to paper 📄](https://www.globalmacrodata.com/research-paper.html)
-
-This repository complements paper, **Müller, Xu, Lehbib, and Chen (2025)**, which introduces a panel dataset of **46 macroeconomic variables across 243 countries** from historical records beginning in the year **1086** until **2024**, including projections through the year **2030**.
-
-## Features
-
-- **Unparalleled Coverage**: Combines data from **32 contemporary sources** (e.g., IMF, World Bank, OECD) with **78 historical datasets**.
-- **Extensive Variables**: GDP, inflation, government finance, trade, employment, interest rates, and more.
-- **Harmonized Data**: Resolves inconsistencies and splices all available data together.
-- **Scheduled Updates**: Regular releases ensure data reliability.
-- **Full Transparency**: All code is open source and available in this repository.
-- **Accessible Formats**: Provided in `.dta`, `.csv` and as **Stata
-/Python/R package**.
-
-## Data access
-
-Download via website
-
-**Python package:**
-```
-pip install global_macro_data
-```
-
-**How to use (examples)**
-```python
-from global_macro_data import gmd
-
-# Get data from latest available version
-df = gmd()
-
-# Get data from a specific version
-df = gmd(version="2025_01")
-
-# Get data for a specific country
-df = gmd(country="USA")
-
-# Get data for multiple countries
-df = gmd(country=["USA", "CHN", "DEU"])
-
-# Get specific variables
-df = gmd(variables=["rGDP", "infl", "unemp"])
-
-# Get raw data for a single variable
-df = gmd(variables="rGDP", raw=True)
-
-# List available variables and their descriptions
-gmd(vars=True)
-
-# List available countries and their ISO codes
-gmd(iso=True)
-
-# Combine parameters
-df = gmd(
- version="2025_01",
- country=["USA", "CHN"],
- variables=["rGDP", "unemp", "CPI"]
-)
-```
-
-## Parameters
-- **variables (str or list)**: Variable code(s) to include (e.g., "rGDP" or ["rGDP", "unemp"])
-- **country (str or list)**: ISO3 country code(s) (e.g., "SGP" or ["MRT", "SGP"])
-- **version (str)**: Dataset version in format 'YYYY_MM' (e.g., '2025_01'). If None or "current", uses the latest version
-- **raw (bool)**: If True, download raw data for a single variable
-- **iso (bool)**: If True, display list of available countries
-- **vars (bool)**: If True, display list of available variables
-
-## Release schedule
-| Release Date | Details |
-|--------------|-----------------|
-| 2025-01-30 | Initial release: 2025_01 |
-| 2025-04-01 | 2025_03 |
-| 2025-07-01 | 2025_06 |
-| 2025-10-01 | 2025_09 |
-| 2026-01-01 | 2025_12 |
-
-## Citation
-
-To cite this dataset, please use the following reference:
-
-```bibtex
-@techreport{mueller2025global,
- title = {The Global Macro Database: A New International Macroeconomic Dataset},
- author = {Müller, Karsten and Xu, Chenzi and Lehbib, Mohamed and Chen, Ziliang},
- year = {2025},
- type = {Working Paper}
-}
-```
-
-## Acknowledgments
-
-The development of the Global Macro Database would not have been possible without the generous funding provided by the Singapore Ministry of Education (MOE) through the PYP grants (WBS A-0003319-01-00 and A-0003319-02-00), a Tier 1 grant (A-8001749- 00-00), and the NUS Risk Management Institute (A-8002360-00-00). This financial support laid the foundation for the successful completion of this extensive project.
diff --git a/global_macro_data.egg-info/SOURCES.txt b/global_macro_data.egg-info/SOURCES.txt
deleted file mode 100644
index 85e548b..0000000
--- a/global_macro_data.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-README.md
-setup.py
-global_macro_data/__init__.py
-global_macro_data/gmd.py
-global_macro_data.egg-info/PKG-INFO
-global_macro_data.egg-info/SOURCES.txt
-global_macro_data.egg-info/dependency_links.txt
-global_macro_data.egg-info/requires.txt
-global_macro_data.egg-info/top_level.txt
-tests/test_gmd.py
\ No newline at end of file
diff --git a/global_macro_data.egg-info/dependency_links.txt b/global_macro_data.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/global_macro_data.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/global_macro_data.egg-info/requires.txt b/global_macro_data.egg-info/requires.txt
deleted file mode 100644
index 65a42be..0000000
--- a/global_macro_data.egg-info/requires.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-requests
-pandas
diff --git a/global_macro_data.egg-info/top_level.txt b/global_macro_data.egg-info/top_level.txt
deleted file mode 100644
index d4a9e38..0000000
--- a/global_macro_data.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-global_macro_data
diff --git a/global_macro_data/__init__.py b/global_macro_data/__init__.py
index ff336e1..8ea010d 100644
--- a/global_macro_data/__init__.py
+++ b/global_macro_data/__init__.py
@@ -1,17 +1,19 @@
-from .gmd import (
- gmd,
- get_available_versions,
+from global_macro_data.gmd import (
+ get_data,
+ list_versions,
get_current_version,
list_variables,
list_countries,
VALID_VARIABLES
)
+from global_macro_data.logging import enable_verbose_logging
__all__ = [
- "gmd",
- "get_available_versions",
+ "get_data",
+ "list_versions",
"get_current_version",
"list_variables",
"list_countries",
+ "enable_verbose_logging",
"VALID_VARIABLES"
-]
\ No newline at end of file
+]
diff --git a/global_macro_data/exceptions.py b/global_macro_data/exceptions.py
new file mode 100644
index 0000000..e02f56b
--- /dev/null
+++ b/global_macro_data/exceptions.py
@@ -0,0 +1,90 @@
+from typing import List
+
+
+INTRO = (
+ "\nGlobal Macro Database by Müller et al. (2025)\n"
+ "Website: https://www.globalmacrodata.com\n\n"
+)
+
+
+class InvalidVariableError(ValueError):
+ """Raised when one or more variable codes are invalid."""
+
+ def __init__(self, invalid_vars: List[str]):
+ self.invalid_vars = invalid_vars
+
+ # Format variable list nicely
+ if len(invalid_vars) == 1:
+ var_list = invalid_vars[0]
+ var_intro = "Invalid variable code"
+ else:
+ var_list = ', '.join(invalid_vars)
+ var_intro = "Invalid variable codes"
+
+ message = (
+ INTRO +
+ f"{var_intro}: {var_list}\n\n"
+ "To see the list of valid variable codes, "
+ "use: gmd.list_variables()"
+ )
+
+ super().__init__(message)
+
+
+class InvalidVersionError(ValueError):
+ """Raised when a requested dataset version does not exist."""
+
+ def __init__(self, requested_version: str):
+
+ self.requested_version = requested_version
+ message = (
+ INTRO +
+ f"Error: '{requested_version}' is not a valid dataset version.\n"
+ "To see the list of valid versions, use: gmd.list_versions()"
+ )
+ super().__init__(message)
+
+
+class RawModeError(ValueError):
+ """Raised when raw=True is used incorrectly."""
+
+ def __init__(self):
+ message = (
+ INTRO +
+ "'raw=True' requires specifying exactly one variable.\n"
+ "Raw data is only accessed variable-wise using: "
+ "gmd.get_data(variable, raw=True)\n"
+ "For full documentation: https://www.globalmacrodata.com/GMD.xlsx"
+ )
+ super().__init__(message)
+
+
+class DataDownloadError(ConnectionError):
+ """Raised when data cannot be downloaded from the remote source."""
+
+ def __init__(self, original_exception: Exception):
+ message = (
+ INTRO +
+ f"Error downloading data:\n{original_exception}"
+ )
+ super().__init__(message)
+ self.original_exception = original_exception
+
+
+class InvalidCountryError(ValueError):
+ """Raised when one or more country codes are invalid."""
+
+ def __init__(self, invalid_codes: List[str]):
+ self.invalid_codes = invalid_codes
+
+ if len(invalid_codes) == 1:
+ msg = f"Invalid country code: '{invalid_codes[0]}'"
+ else:
+ msg = f"Invalid country codes: {', '.join(invalid_codes)}"
+
+ message = (
+ INTRO +
+ f"{msg}\n\n"
+ "To see the list of valid country codes, use: gmd.list_countries()"
+ )
+ super().__init__(message)
diff --git a/global_macro_data/gmd.py b/global_macro_data/gmd.py
index aa78e7f..42f940e 100644
--- a/global_macro_data/gmd.py
+++ b/global_macro_data/gmd.py
@@ -1,9 +1,24 @@
-import os
-import requests
-import pandas as pd
+# Standard library
import io
-from typing import Optional, Union, List
-import sys
+from typing import Optional, Union, List, Dict
+try:
+ import importlib.resources as importlib_resources
+except ImportError:
+ import importlib_resources # Backport for Python <3.9
+
+import json
+
+# Third-party
+import pandas as pd
+import requests
+
+# Internal modules
+from .logging import logger
+from .exceptions import (
+ InvalidVariableError, InvalidVersionError, RawModeError,
+ DataDownloadError, InvalidCountryError
+)
+
# Valid variables list
VALID_VARIABLES = [
@@ -33,7 +48,8 @@
"CurrencyCrisis", "BankingCrisis", "SovDebtCrisis"
]
-def get_available_versions() -> List[str]:
+
+def list_versions() -> List[str]:
"""Get list of available versions from GitHub"""
try:
versions_url = (
@@ -43,154 +59,150 @@ def get_available_versions() -> List[str]:
response = requests.get(versions_url)
if response.status_code != 200:
raise Exception("Could not fetch versions")
-
+
versions_df = pd.read_csv(io.StringIO(response.text))
versions = versions_df['versions'].tolist()
return sorted(versions, reverse=True)
except Exception as e:
raise Exception(f"Error fetching versions: {str(e)}")
+
def get_current_version() -> str:
"""Get the current version of the dataset"""
- versions = get_available_versions()
+ versions = list_versions()
return versions[0] if versions else None
-def list_variables() -> None:
- """Display list of available variables and their descriptions"""
- print("\nAvailable variables:\n")
- print("-" * 90)
- print(f"{'Variable':<17} Description")
- print("-" * 90)
-
+
+def list_variables(
+ as_dict: bool = False
+) -> Union[pd.DataFrame, Dict[str, str]]:
+ """Return available variable codes and their descriptions."""
+ global VALID_VARIABLES
descriptions = {
- "nGDP": "Nominal Gross Domestic Product",
- "rGDP": "Real Gross Domestic Product, in 2010 prices",
- "rGDP_pc": "Real Gross Domestic Product per Capita",
- "rGDP_USD": "Real Gross Domestic Product in USD",
- "deflator": "GDP deflator",
- "cons": "Total Consumption",
- "rcons": "Real Total Consumption",
- "cons_GDP": "Total Consumption as % of GDP",
- "inv": "Total Investment",
- "inv_GDP": "Total Investment as % of GDP",
- "finv": "Fixed Investment",
- "finv_GDP": "Fixed Investment as % of GDP",
- "exports": "Total Exports",
- "exports_GDP": "Total Exports as % of GDP",
- "imports": "Total Imports",
- "imports_GDP": "Total Imports as % of GDP",
- "CA": "Current Account Balance",
- "CA_GDP": "Current Account Balance as % of GDP",
- "USDfx": "Exchange Rate against USD",
- "REER": "Real Effective Exchange Rate, 2010 = 100",
- "govexp": "Government Expenditure",
- "govexp_GDP": "Government Expenditure as % of GDP",
- "govrev": "Government Revenue",
- "govrev_GDP": "Government Revenue as % of GDP",
- "govtax": "Government Tax Revenue",
- "govtax_GDP": "Government Tax Revenue as % of GDP",
- "govdef": "Government Deficit",
- "govdef_GDP": "Government Deficit as % of GDP",
- "govdebt": "Government Debt",
- "govdebt_GDP": "Government Debt as % of GDP",
- "HPI": "House Price Index",
- "CPI": "Consumer Price Index, 2010 = 100",
- "infl": "Inflation Rate",
- "pop": "Population",
- "unemp": "Unemployment Rate",
- "strate": "Short-term Interest Rate",
- "ltrate": "Long-term Interest Rate",
- "cbrate": "Central Bank Policy Rate",
- "M0": "M0 Money Supply",
- "M1": "M1 Money Supply",
- "M2": "M2 Money Supply",
- "M3": "M3 Money Supply",
- "M4": "M4 Money Supply",
- "SovDebtCrisis": "Sovereign Debt Crisis",
- "CurrencyCrisis": "Currency Crisis",
- "BankingCrisis": "Banking Crisis"
+ 'nGDP': 'Nominal Gross Domestic Product',
+ 'rGDP': 'Real Gross Domestic Product, in 2010 prices',
+ 'rGDP_pc': 'Real Gross Domestic Product per Capita',
+ 'rGDP_USD': 'Real Gross Domestic Product in USD',
+ 'deflator': 'GDP deflator',
+ 'cons': 'Total Consumption',
+ 'rcons': 'Real Total Consumption',
+ 'cons_GDP': 'Total Consumption as % of GDP',
+ 'inv': 'Total Investment',
+ 'inv_GDP': 'Total Investment as % of GDP',
+ 'finv': 'Fixed Investment',
+ 'finv_GDP': 'Fixed Investment as % of GDP',
+ 'exports': 'Total Exports',
+ 'exports_GDP': 'Total Exports as % of GDP',
+ 'imports': 'Total Imports',
+ 'imports_GDP': 'Total Imports as % of GDP',
+ 'CA': 'Current Account Balance',
+ 'CA_GDP': 'Current Account Balance as % of GDP',
+ 'USDfx': 'Exchange Rate against USD',
+ 'REER': 'Real Effective Exchange Rate, 2010 = 100',
+ 'govexp': 'Government Expenditure',
+ 'govexp_GDP': 'Government Expenditure as % of GDP',
+ 'govrev': 'Government Revenue',
+ 'govrev_GDP': 'Government Revenue as % of GDP',
+ 'govtax': 'Government Tax Revenue',
+ 'govtax_GDP': 'Government Tax Revenue as % of GDP',
+ 'govdef': 'Government Deficit',
+ 'govdef_GDP': 'Government Deficit as % of GDP',
+ 'govdebt': 'Government Debt',
+ 'govdebt_GDP': 'Government Debt as % of GDP',
+ 'HPI': 'House Price Index',
+ 'CPI': 'Consumer Price Index, 2010 = 100',
+ 'infl': 'Inflation Rate',
+ 'pop': 'Population',
+ 'unemp': 'Unemployment Rate',
+ 'strate': 'Short-term Interest Rate',
+ 'ltrate': 'Long-term Interest Rate',
+ 'cbrate': 'Central Bank Policy Rate',
+ 'M0': 'M0 Money Supply',
+ 'M1': 'M1 Money Supply',
+ 'M2': 'M2 Money Supply',
+ 'M3': 'M3 Money Supply',
+ 'M4': 'M4 Money Supply',
+ 'SovDebtCrisis': 'Sovereign Debt Crisis',
+ 'CurrencyCrisis': 'Currency Crisis',
+ 'BankingCrisis': 'Banking Crisis',
}
-
- for var in sorted(VALID_VARIABLES):
- print(f"{var:<17} {descriptions.get(var, '')}")
-
- print("-" * 90)
-
-def list_countries() -> None:
- """Display list of available countries and their ISO codes"""
+
+ data = pd.DataFrame({
+ 'Variable': VALID_VARIABLES,
+ 'Description': [descriptions.get(var, '') for var in VALID_VARIABLES]
+ }).sort_values('Variable').reset_index(drop=True)
+
+ if as_dict:
+ return data.set_index('Variable').to_dict()
+
+ return data
+
+
+def list_countries(
+ as_dict: bool = False
+) -> Union[pd.DataFrame, Dict[str, str]]:
+ """Return countries and their ISO3 codes."""
try:
- # Load isomapping from the package directory
- script_dir = os.path.dirname(os.path.abspath(__file__))
- isomapping_path = os.path.join(
- os.path.dirname(script_dir), 'isomapping.csv'
+ with importlib_resources.open_text('global_macro_data',
+ 'isomapping.json',
+ encoding='utf-8') as f:
+
+ data = json.load(f)
+
+ if as_dict:
+ return data
+
+ return pd.DataFrame(
+ {'Country': list(data.keys()),
+ 'ISO3': list(data.values())
+ }
)
- isomapping = pd.read_csv(isomapping_path)
-
- print("\nCountry and territories" + " " * 27 + "Code")
- print("-" * 60)
-
- for _, row in isomapping.iterrows():
- print(f"{row['countryname']:<50} {row['ISO3']}")
-
- print("-" * 60)
except Exception as e:
- raise Exception(f"Error loading country list: {str(e)}")
+ raise RuntimeError(f'Error loading country list: {e}')
+
-def gmd(
+def get_data(
variables: Optional[Union[str, List[str]]] = None,
country: Optional[Union[str, List[str]]] = None,
version: Optional[str] = None,
raw: bool = False,
- iso: bool = False,
- vars: bool = False
-) -> Optional[pd.DataFrame]:
+) -> pd.DataFrame:
"""
Download and filter Global Macro Data.
-
- Parameters:
- - variables (str or list): Variable code(s) to include
- (e.g., "rGDP" or ["rGDP", "unemp"])
- - country (str or list): ISO3 country code(s)
- (e.g., "SGP" or ["MRT", "SGP"])
- - version (str): Dataset version in format 'YYYY_MM'
- (e.g., '2025_01')
- - raw (bool): If True, download raw data for a single variable
- - iso (bool): If True, display list of available countries
- - vars (bool): If True, display list of available variables
-
- Returns:
- - pd.DataFrame: The requested data, or None if displaying lists
+
+ Parameters
+ ----------
+ variables : str or list of str, optional
+ Variable code(s) to include (e.g., 'rGDP' or ['rGDP', 'unemp']).
+ country : str or list of str, optional
+ Country or ISO3 country code(s) to include (e.g., 'SGP' or
+ ['MRT', 'SGP']).
+ version : str, optional
+ Dataset version in 'YYYY_MM' format (e.g., '2025_01').
+ raw : bool, default=False
+ If True, download raw data for a single variable only.
+
+ Returns
+ -------
+ pd.DataFrame or None
+ Filtered macroeconomic data as a DataFrame, or None if no data
+ available.
"""
+ global VALID_VARIABLES
base_url = "https://www.globalmacrodata.com"
-
- # Handle special display options
- if iso:
- list_countries()
- return None
-
- if vars:
- list_variables()
- return None
-
+
# Validate variables before proceeding
if variables:
if isinstance(variables, str):
variables = [variables]
-
+
# Validate variables
invalid_vars = [
var for var in variables if var not in VALID_VARIABLES
]
if invalid_vars:
- print("Global Macro Database by Müller et. al (2025)")
- print("Website: https://www.globalmacrodata.com\n")
- print(f"Invalid variable code: {invalid_vars[0]}")
- print(
- "\nTo see the list of valid variable codes, "
- "use: gmd(vars=True)"
- )
- sys.exit(1)
-
+ raise InvalidVariableError(invalid_vars)
+
# Get current version if not specified
if version is None:
version = get_current_version()
@@ -198,112 +210,105 @@ def gmd(
version = get_current_version()
else:
# Check if version exists
- available_versions = get_available_versions()
+ available_versions = list_versions()
if version not in available_versions:
- print("Global Macro Database by Müller et. al (2025)")
- print("Website: https://www.globalmacrodata.com\n")
- print(f"Error: {version} is not valid")
- print(f"Available versions are: {', '.join(available_versions)}")
- print(f"The current version is: {get_current_version()}")
- sys.exit(1)
-
+ raise InvalidVersionError(
+ requested_version=version,
+ )
+
# Handle raw data option
if raw:
- if (not variables or
- (isinstance(variables, list) and len(variables) > 1)):
- print("Global Macro Database by Müller et. al (2025)")
- print("Website: https://www.globalmacrodata.com\n")
- print("Warning: raw requires specifying exactly one variable")
- print("Note: Raw data is only accessed variable-wise using: gmd [variable], raw")
- print("To download the full data documentation: https://www.globalmacrodata.com/GMD.xlsx")
- sys.exit(1)
-
+ if not variables or \
+ (isinstance(variables, list) and len(variables) > 1):
+ raise RawModeError()
+
if isinstance(variables, list):
variables = variables[0]
-
+
data_url = f"{base_url}/{variables}_{version}.csv"
- print(f"Importing raw data for variable: {variables}")
+ logger.info(f'Importing raw data for variable: {variables}')
else:
# Handle single variable case for efficiency
if isinstance(variables, list) and len(variables) == 1:
variables = variables[0]
data_url = f"{base_url}/{variables}_{version}.csv"
- print(f"Importing data for variable: {variables}")
+ logger.info(f'Importing data for variable: {variables}')
else:
data_url = f"{base_url}/GMD_{version}.csv"
-
+ logger.info('Importing data')
+
# Download data
try:
response = requests.get(data_url)
response.raise_for_status()
except requests.exceptions.RequestException as e:
- print("Global Macro Database by Müller et. al (2025)")
- print("Website: https://www.globalmacrodata.com\n")
- print(f"Error downloading data: {str(e)}")
- sys.exit(1)
-
+ raise DataDownloadError(e)
+
# Read the data
df = pd.read_csv(io.StringIO(response.text))
-
+
# Filter by country if specified
if country:
if isinstance(country, str):
country = [country]
-
- country = [c.upper() for c in country]
-
+
+ # Load country name to ISO3 mapping
+ country_to_ISO = {
+ k.upper(): v for k, v in list_countries(as_dict=True).items()
+ }
+ country = [country_to_ISO.get(c.upper(), c.upper()) for c in country]
+
# Validate country codes
invalid_countries = [
c for c in country if c not in df["ISO3"].unique()
]
if invalid_countries:
- print("Global Macro Database by Müller et. al (2025)")
- print("Website: https://www.globalmacrodata.com\n")
- print(f"Error: Invalid country code '{invalid_countries[0]}'")
- print("\nTo see the list of valid country codes, use: gmd(iso=True)")
- sys.exit(1)
-
+ raise InvalidCountryError(invalid_countries)
+
df = df[df["ISO3"].isin(country)]
- print(f"Filtered data for countries: {', '.join(country)}")
-
+ logger.info(f"Filtered data for countries: {', '.join(country)}")
+
# Filter by variables if specified
if variables and not raw:
if isinstance(variables, str):
variables = [variables]
-
+
# Always include identifier columns
required_cols = ["ISO3", "countryname", "year"]
all_cols = required_cols + [
var for var in variables if var not in required_cols
]
-
+
# Filter to only include requested variables
existing_vars = [var for var in all_cols if var in df.columns]
df = df[existing_vars]
-
+
# Clean up missing variables
df = df.dropna(axis=1, how='all')
-
+
# Display dataset information
if len(df) == 0:
- print(f"The database has no data on {variables} for {country}")
+ logger.warning("The database has no data on "
+ f"{variables} for {country}")
return None
-
+
if raw:
- n_sources = len(df.columns) - 8 # Subtract identifier columns
- print(f"Final dataset: {len(df)} observations of {n_sources} sources")
+ n_sources = len(df.columns) - 7 # Subtract identifier columns
+ logger.info(f"Final dataset: {len(df)} "
+ f"observations of {n_sources} sources")
else:
- print(
+ logger.info(
f"Final dataset: {len(df)} observations of "
f"{len(df.columns)} variables"
)
-
- print(f"Version: {version}")
-
+
+ logger.info(f"Version: {version}")
+
# Sort and order columns
df = df.sort_values(['countryname', 'year'])
id_cols = ['ISO3', 'countryname', 'year']
- other_cols = [col for col in df.columns if col not in id_cols]
+ other_cols = [col for col in df.columns
+ if (col not in id_cols)]
df = df[id_cols + other_cols]
-
- return df
\ No newline at end of file
+
+ return df.drop(columns=['id'], errors='ignore').reset_index(drop=True)
diff --git a/global_macro_data/isomapping.json b/global_macro_data/isomapping.json
new file mode 100644
index 0000000..d2ee849
--- /dev/null
+++ b/global_macro_data/isomapping.json
@@ -0,0 +1,259 @@
+{
+ "Afghanistan": "AFG",
+ "Albania": "ALB",
+ "Algeria": "DZA",
+ "American Samoa": "ASM",
+ "Andorra": "AND",
+ "Angola": "AGO",
+ "Anguilla": "AIA",
+ "Antarctica": "ATA",
+ "Antigua and Barbuda": "ATG",
+ "Argentina": "ARG",
+ "Armenia": "ARM",
+ "Aruba": "ABW",
+ "Australia": "AUS",
+ "Austria": "AUT",
+ "Azerbaijan": "AZE",
+ "Bahamas": "BHS",
+ "Bahrain": "BHR",
+ "Bangladesh": "BGD",
+ "Barbados": "BRB",
+ "Belarus": "BLR",
+ "Belgium": "BEL",
+ "Belize": "BLZ",
+ "Benin": "BEN",
+ "Bermuda": "BMU",
+ "Bhutan": "BTN",
+ "Bolivia": "BOL",
+ "Bonaire, Sint Eustatius and Saba": "BES",
+ "Bosnia and Herzegovina": "BIH",
+ "Botswana": "BWA",
+ "Bouvet Island": "BVT",
+ "Brazil": "BRA",
+ "British Indian Ocean Territory": "IOT",
+ "British Virgin Islands": "VGB",
+ "Brunei": "BRN",
+ "Bulgaria": "BGR",
+ "Burkina Faso": "BFA",
+ "Burundi": "BDI",
+ "Cambodia": "KHM",
+ "Cameroon": "CMR",
+ "Canada": "CAN",
+ "Cape Verde": "CPV",
+ "Cayman Islands": "CYM",
+ "Central African Republic": "CAF",
+ "Chad": "TCD",
+ "Chile": "CHL",
+ "China": "CHN",
+ "Christmas Island": "CXR",
+ "Cocos (Keeling) Islands": "CCK",
+ "Colombia": "COL",
+ "Comoros": "COM",
+ "Cook Islands": "COK",
+ "Costa Rica": "CRI",
+ "Croatia": "HRV",
+ "Cuba": "CUB",
+ "Curaçao": "CUW",
+ "Cyprus": "CYP",
+ "Czech Republic": "CZE",
+ "Czechoslovakia": "CSK",
+ "Democratic Republic of Yemen": "YMD",
+ "Democratic Republic of the Congo": "COD",
+ "Denmark": "DNK",
+ "Djibouti": "DJI",
+ "Dominica": "DMA",
+ "Dominican Republic": "DOM",
+ "Ecuador": "ECU",
+ "Egypt": "EGY",
+ "El Salvador": "SLV",
+ "Equatorial Guinea": "GNQ",
+ "Eritrea": "ERI",
+ "Estonia": "EST",
+ "Eswatini": "SWZ",
+ "Ethiopia": "ETH",
+ "Falkland Islands": "FLK",
+ "Faroe Islands": "FRO",
+ "Fiji": "FJI",
+ "Finland": "FIN",
+ "France": "FRA",
+ "French Guiana": "GUF",
+ "French Polynesia": "PYF",
+ "French Southern Territories": "ATF",
+ "Gabon": "GAB",
+ "Gambia": "GMB",
+ "Georgia": "GEO",
+ "German Democratic Republic": "DDR",
+ "Germany": "DEU",
+ "Ghana": "GHA",
+ "Gibraltar": "GIB",
+ "Greece": "GRC",
+ "Greenland": "GRL",
+ "Grenada": "GRD",
+ "Guadeloupe": "GLP",
+ "Guam": "GUM",
+ "Guatemala": "GTM",
+ "Guernsey": "GGY",
+ "Guinea": "GIN",
+ "Guinea-Bissau": "GNB",
+ "Guyana": "GUY",
+ "Haiti": "HTI",
+ "Heard Island and McDonald Islands": "HMD",
+ "Holy See": "VAT",
+ "Honduras": "HND",
+ "Hong Kong": "HKG",
+ "Hungary": "HUN",
+ "Iceland": "ISL",
+ "India": "IND",
+ "Indonesia": "IDN",
+ "Iran": "IRN",
+ "Iraq": "IRQ",
+ "Ireland": "IRL",
+ "Isle of Man": "IMN",
+ "Israel": "ISR",
+ "Italy": "ITA",
+ "Ivory Coast": "CIV",
+ "Jamaica": "JAM",
+ "Japan": "JPN",
+ "Jersey": "JEY",
+ "Jordan": "JOR",
+ "Kazakhstan": "KAZ",
+ "Kenya": "KEN",
+ "Kiribati": "KIR",
+ "Kosovo": "XKX",
+ "Kuwait": "KWT",
+ "Kyrgyzstan": "KGZ",
+ "Laos": "LAO",
+ "Latvia": "LVA",
+ "Lebanon": "LBN",
+ "Lesotho": "LSO",
+ "Liberia": "LBR",
+ "Libya": "LBY",
+ "Liechtenstein": "LIE",
+ "Lithuania": "LTU",
+ "Luxembourg": "LUX",
+ "Macau": "MAC",
+ "Macedonia": "MKD",
+ "Madagascar": "MDG",
+ "Malawi": "MWI",
+ "Malaysia": "MYS",
+ "Maldives": "MDV",
+ "Mali": "MLI",
+ "Malta": "MLT",
+ "Marshall Islands": "MHL",
+ "Martinique": "MTQ",
+ "Mauritania": "MRT",
+ "Mauritius": "MUS",
+ "Mayotte": "MYT",
+ "Mexico": "MEX",
+ "Micronesia (Federated States of)": "FSM",
+ "Moldova": "MDA",
+ "Monaco": "MCO",
+ "Mongolia": "MNG",
+ "Montenegro": "MNE",
+ "Montserrat": "MSR",
+ "Morocco": "MAR",
+ "Mozambique": "MOZ",
+ "Myanmar": "MMR",
+ "Namibia": "NAM",
+ "Nauru": "NRU",
+ "Nepal": "NPL",
+ "Netherlands": "NLD",
+ "Netherlands Antilles": "ANT",
+ "New Caledonia": "NCL",
+ "New Zealand": "NZL",
+ "Nicaragua": "NIC",
+ "Niger": "NER",
+ "Nigeria": "NGA",
+ "Niue": "NIU",
+ "Norfolk Island": "NFK",
+ "North Korea": "PRK",
+ "Northern Mariana Islands": "MNP",
+ "Norway": "NOR",
+ "Oman": "OMN",
+ "Pakistan": "PAK",
+ "Palau": "PLW",
+ "Palestine": "PSE",
+ "Panama": "PAN",
+ "Papua New Guinea": "PNG",
+ "Paraguay": "PRY",
+ "Peru": "PER",
+ "Philippines": "PHL",
+ "Pitcairn": "PCN",
+ "Poland": "POL",
+ "Portugal": "PRT",
+ "Puerto Rico": "PRI",
+ "Qatar": "QAT",
+ "Republic of the Congo": "COG",
+ "Romania": "ROU",
+ "Russian Federation": "RUS",
+ "Rwanda": "RWA",
+ "Réunion": "REU",
+ "Saint Barthélemy": "BLM",
+ "Saint Helena, Ascension and Tristan da Cunha": "SHN",
+ "Saint Kitts and Nevis": "KNA",
+ "Saint Lucia": "LCA",
+ "Saint Martin": "MAF",
+ "Saint Pierre and Miquelon": "SPM",
+ "Saint Vincent and the Grenadines": "VCT",
+ "Samoa": "WSM",
+ "San Marino": "SMR",
+ "Sao Tome and Principe": "STP",
+ "Saudi Arabia": "SAU",
+ "Senegal": "SEN",
+ "Serbia": "SRB",
+ "Serbia and Montenegro": "SCG",
+ "Seychelles": "SYC",
+ "Sierra Leone": "SLE",
+ "Singapore": "SGP",
+ "Sint Maarten": "SXM",
+ "Slovakia": "SVK",
+ "Slovenia": "SVN",
+ "Solomon Islands": "SLB",
+ "Somalia": "SOM",
+ "South Africa": "ZAF",
+ "South Georgia and the South Sandwich Islands": "SGS",
+ "South Korea": "KOR",
+ "South Sudan": "SSD",
+ "Soviet Union": "SUN",
+ "Spain": "ESP",
+ "Sri Lanka": "LKA",
+ "Sudan": "SDN",
+ "Suriname": "SUR",
+ "Svalbard and Jan Mayen": "SJM",
+ "Sweden": "SWE",
+ "Switzerland": "CHE",
+ "Syria": "SYR",
+ "Taiwan": "TWN",
+ "Tajikistan": "TJK",
+ "Tanzania": "TZA",
+ "Thailand": "THA",
+ "Timor-Leste": "TLS",
+ "Togo": "TGO",
+ "Tokelau": "TKL",
+ "Tonga": "TON",
+ "Trinidad and Tobago": "TTO",
+ "Tunisia": "TUN",
+ "Turkey": "TUR",
+ "Turkmenistan": "TKM",
+ "Turks and Caicos Islands": "TCA",
+ "Tuvalu": "TUV",
+ "US Virgin Islands": "VIR",
+ "Uganda": "UGA",
+ "Ukraine": "UKR",
+ "United Arab Emirates": "ARE",
+ "United Kingdom": "GBR",
+ "United States": "USA",
+ "United States Minor Outlying Islands": "UMI",
+ "Uruguay": "URY",
+ "Uzbekistan": "UZB",
+ "Vanuatu": "VUT",
+ "Venezuela": "VEN",
+ "Vietnam": "VNM",
+ "Wallis and Futuna": "WLF",
+ "Western Sahara": "ESH",
+ "Yemen": "YEM",
+ "Yugoslavia": "YUG",
+ "Zambia": "ZMB",
+ "Zimbabwe": "ZWE",
+ "Åland Åland Islands": "ALA"
+}
\ No newline at end of file
diff --git a/global_macro_data/logging.py b/global_macro_data/logging.py
new file mode 100644
index 0000000..d758fa3
--- /dev/null
+++ b/global_macro_data/logging.py
@@ -0,0 +1,21 @@
+import logging
+
+logger = logging.getLogger('gmd')
+logger.setLevel(logging.INFO) # Default level (can be overridden by user)
+
+# Add NullHandler so importing your package doesn't configure root logger
+logger.addHandler(logging.NullHandler())
+
+
+def enable_verbose_logging(level=logging.INFO):
+ """Enable console logging specifically for the gmd package."""
+ handler = logging.StreamHandler()
+ handler.setLevel(level)
+
+ formatter = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
+ handler.setFormatter(formatter)
+
+ if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
+ logger.addHandler(handler)
+
+ logger.setLevel(level)
diff --git a/isomapping.csv b/isomapping.csv
deleted file mode 100644
index 18d6e3c..0000000
--- a/isomapping.csv
+++ /dev/null
@@ -1,258 +0,0 @@
-countryname,ISO3
-Afghanistan,AFG
-Albania,ALB
-Algeria,DZA
-American Samoa,ASM
-Andorra,AND
-Angola,AGO
-Anguilla,AIA
-Antarctica,ATA
-Antigua and Barbuda,ATG
-Argentina,ARG
-Armenia,ARM
-Aruba,ABW
-Australia,AUS
-Austria,AUT
-Azerbaijan,AZE
-Bahamas,BHS
-Bahrain,BHR
-Bangladesh,BGD
-Barbados,BRB
-Belarus,BLR
-Belgium,BEL
-Belize,BLZ
-Benin,BEN
-Bermuda,BMU
-Bhutan,BTN
-Bolivia,BOL
-"Bonaire, Sint Eustatius and Saba",BES
-Bosnia and Herzegovina,BIH
-Botswana,BWA
-Bouvet Island,BVT
-Brazil,BRA
-British Indian Ocean Territory,IOT
-British Virgin Islands,VGB
-Brunei,BRN
-Bulgaria,BGR
-Burkina Faso,BFA
-Burundi,BDI
-Cambodia,KHM
-Cameroon,CMR
-Canada,CAN
-Cape Verde,CPV
-Cayman Islands,CYM
-Central African Republic,CAF
-Chad,TCD
-Chile,CHL
-China,CHN
-Christmas Island,CXR
-Cocos (Keeling) Islands,CCK
-Colombia,COL
-Comoros,COM
-Cook Islands,COK
-Costa Rica,CRI
-Croatia,HRV
-Cuba,CUB
-Curaçao,CUW
-Cyprus,CYP
-Czech Republic,CZE
-Czechoslovakia,CSK
-Democratic Republic of Yemen,YMD
-Democratic Republic of the Congo,COD
-Denmark,DNK
-Djibouti,DJI
-Dominica,DMA
-Dominican Republic,DOM
-Ecuador,ECU
-Egypt,EGY
-El Salvador,SLV
-Equatorial Guinea,GNQ
-Eritrea,ERI
-Estonia,EST
-Eswatini,SWZ
-Ethiopia,ETH
-Falkland Islands,FLK
-Faroe Islands,FRO
-Fiji,FJI
-Finland,FIN
-France,FRA
-French Guiana,GUF
-French Polynesia,PYF
-French Southern Territories,ATF
-Gabon,GAB
-Gambia,GMB
-Georgia,GEO
-German Democratic Republic,DDR
-Germany,DEU
-Ghana,GHA
-Gibraltar,GIB
-Greece,GRC
-Greenland,GRL
-Grenada,GRD
-Guadeloupe,GLP
-Guam,GUM
-Guatemala,GTM
-Guernsey,GGY
-Guinea,GIN
-Guinea-Bissau,GNB
-Guyana,GUY
-Haiti,HTI
-Heard Island and McDonald Islands,HMD
-Holy See,VAT
-Honduras,HND
-Hong Kong,HKG
-Hungary,HUN
-Iceland,ISL
-India,IND
-Indonesia,IDN
-Iran,IRN
-Iraq,IRQ
-Ireland,IRL
-Isle of Man,IMN
-Israel,ISR
-Italy,ITA
-Ivory Coast,CIV
-Jamaica,JAM
-Japan,JPN
-Jersey,JEY
-Jordan,JOR
-Kazakhstan,KAZ
-Kenya,KEN
-Kiribati,KIR
-Kosovo,XKX
-Kuwait,KWT
-Kyrgyzstan,KGZ
-Laos,LAO
-Latvia,LVA
-Lebanon,LBN
-Lesotho,LSO
-Liberia,LBR
-Libya,LBY
-Liechtenstein,LIE
-Lithuania,LTU
-Luxembourg,LUX
-Macau,MAC
-Macedonia,MKD
-Madagascar,MDG
-Malawi,MWI
-Malaysia,MYS
-Maldives,MDV
-Mali,MLI
-Malta,MLT
-Marshall Islands,MHL
-Martinique,MTQ
-Mauritania,MRT
-Mauritius,MUS
-Mayotte,MYT
-Mexico,MEX
-Micronesia (Federated States of),FSM
-Moldova,MDA
-Monaco,MCO
-Mongolia,MNG
-Montenegro,MNE
-Montserrat,MSR
-Morocco,MAR
-Mozambique,MOZ
-Myanmar,MMR
-Namibia,NAM
-Nauru,NRU
-Nepal,NPL
-Netherlands,NLD
-Netherlands Antilles,ANT
-New Caledonia,NCL
-New Zealand,NZL
-Nicaragua,NIC
-Niger,NER
-Nigeria,NGA
-Niue,NIU
-Norfolk Island,NFK
-North Korea,PRK
-Northern Mariana Islands,MNP
-Norway,NOR
-Oman,OMN
-Pakistan,PAK
-Palau,PLW
-Palestine,PSE
-Panama,PAN
-Papua New Guinea,PNG
-Paraguay,PRY
-Peru,PER
-Philippines,PHL
-Pitcairn,PCN
-Poland,POL
-Portugal,PRT
-Puerto Rico,PRI
-Qatar,QAT
-Republic of the Congo,COG
-Romania,ROU
-Russian Federation,RUS
-Rwanda,RWA
-Réunion,REU
-Saint Barthélemy,BLM
-"Saint Helena, Ascension and Tristan da Cunha",SHN
-Saint Kitts and Nevis,KNA
-Saint Lucia,LCA
-Saint Martin,MAF
-Saint Pierre and Miquelon,SPM
-Saint Vincent and the Grenadines,VCT
-Samoa,WSM
-San Marino,SMR
-Sao Tome and Principe,STP
-Saudi Arabia,SAU
-Senegal,SEN
-Serbia,SRB
-Serbia and Montenegro,SCG
-Seychelles,SYC
-Sierra Leone,SLE
-Singapore,SGP
-Sint Maarten,SXM
-Slovakia,SVK
-Slovenia,SVN
-Solomon Islands,SLB
-Somalia,SOM
-South Africa,ZAF
-South Georgia and the South Sandwich Islands,SGS
-South Korea,KOR
-South Sudan,SSD
-Soviet Union,SUN
-Spain,ESP
-Sri Lanka,LKA
-Sudan,SDN
-Suriname,SUR
-Svalbard and Jan Mayen,SJM
-Sweden,SWE
-Switzerland,CHE
-Syria,SYR
-Taiwan,TWN
-Tajikistan,TJK
-Tanzania,TZA
-Thailand,THA
-Timor-Leste,TLS
-Togo,TGO
-Tokelau,TKL
-Tonga,TON
-Trinidad and Tobago,TTO
-Tunisia,TUN
-Turkey,TUR
-Turkmenistan,TKM
-Turks and Caicos Islands,TCA
-Tuvalu,TUV
-US Virgin Islands,VIR
-Uganda,UGA
-Ukraine,UKR
-United Arab Emirates,ARE
-United Kingdom,GBR
-United States,USA
-United States Minor Outlying Islands,UMI
-Uruguay,URY
-Uzbekistan,UZB
-Vanuatu,VUT
-Venezuela,VEN
-Vietnam,VNM
-Wallis and Futuna,WLF
-Western Sahara,ESH
-Yemen,YEM
-Yugoslavia,YUG
-Zambia,ZMB
-Zimbabwe,ZWE
-Åland Åland Islands,ALA
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..727ac0a
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,34 @@
+[project]
+name = "global-macro-data"
+version = "1.0.0"
+description = "Global Macro Database by Karsten Müller, Chenzi Xu, Mohamed Lehbib and Ziliang Chen (2025)"
+readme = "README.md"
+authors = [
+ { name = "Yangbo Wang", email = "wangyangbo@ruc.edu.cn" }
+]
+requires-python = ">=3.8"
+license = { text = "MIT" }
+dependencies = [
+ "pandas>=1.0.5",
+ "requests>=2.20",
+ "importlib_resources; python_version < \"3.9\""
+]
+
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent"
+]
+
+[build-system]
+requires = ["setuptools>=42", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools]
+packages = ["global_macro_data"]
+
+[tool.setuptools.package-data]
+global_macro_data = ["isomapping.json"]
+
+[project.urls]
+Homepage = "https://github.com/KMueller-Lab/Global-Macro-Database-Python"
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 4c3088f..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-requests
-pandas
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 0639e32..0000000
--- a/setup.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import os
-
-def read_readme():
- if os.path.exists("README.md"):
- with open("README.md", "r", encoding="utf-8") as f:
- return f.read()
- return "Global Macro Data package"
-
-from setuptools import setup, find_packages
-
-setup(
- name="global-macro-data",
- version="0.3.1",
- packages=find_packages(),
- package_data={
- "global_macro_data": ["isomapping.csv"],
- },
- install_requires=[
- "requests",
- "pandas"
- ],
- author="Yangbo Wang",
- author_email="wangyangbo@ruc.edu.cn",
- description="Global Macro Database by Karsten Müller, Chenzi Xu, Mohamed Lehbib and Ziliang Chen (2025)",
- long_description=open("README.md", encoding="utf-8").read(),
- long_description_content_type="text/markdown",
- url="https://github.com/KMueller-Lab/Global-Macro-Database-Python",
- classifiers=[
- "Programming Language :: Python :: 3",
- "License :: OSI Approved :: MIT License",
- "Operating System :: OS Independent",
- ],
- python_requires=">=3.6",
-)
diff --git a/tests/conftest.py b/tests/conftest.py
deleted file mode 100644
index 0208376..0000000
--- a/tests/conftest.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import pytest
-import pandas as pd
-import os
-import sys
-
-# Add the package root directory to Python path
-sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
-
-@pytest.fixture(scope="session")
-def sample_data():
- """Create a sample dataset for testing"""
- return pd.DataFrame({
- 'ISO3': ['USA', 'CHN', 'DEU'] * 2,
- 'countryname': ['United States', 'China', 'Germany'] * 2,
- 'year': [2020, 2020, 2020, 2021, 2021, 2021],
- 'rGDP': [1000, 800, 600, 1050, 850, 620],
- 'infl': [2.0, 3.0, 1.5, 2.5, 3.5, 1.8]
- })
\ No newline at end of file
diff --git a/tests/test_gmd.py b/tests/test_gmd.py
index 1839c00..ef9e4a5 100644
--- a/tests/test_gmd.py
+++ b/tests/test_gmd.py
@@ -1,89 +1,96 @@
import pytest
import pandas as pd
-from global_macro_data import (
- gmd,
- get_available_versions,
- get_current_version,
- list_variables,
- list_countries,
- VALID_VARIABLES
+import global_macro_data as gmd
+from global_macro_data.exceptions import (
+ InvalidVersionError,
+ InvalidCountryError,
+ InvalidVariableError,
+ RawModeError,
)
+
def test_get_available_versions():
"""Test getting available versions"""
- versions = get_available_versions()
+ versions = gmd.list_versions()
assert isinstance(versions, list)
assert len(versions) > 0
assert all(isinstance(v, str) for v in versions)
assert all(len(v.split('_')) == 2 for v in versions)
+
def test_get_current_version():
"""Test getting current version"""
- version = get_current_version()
+ version = gmd.get_current_version()
assert isinstance(version, str)
assert len(version.split('_')) == 2
-def test_list_variables(capsys):
- """Test listing variables"""
- list_variables()
- captured = capsys.readouterr()
- assert "Available variables" in captured.out
- for var in VALID_VARIABLES:
- assert var in captured.out
-
-def test_list_countries(capsys):
- """Test listing countries"""
- list_countries()
- captured = capsys.readouterr()
- assert "Country and territories" in captured.out
- assert "Code" in captured.out
+
+def test_list_variables():
+ df = gmd.list_variables()
+ assert isinstance(df, pd.DataFrame)
+ assert not df.empty
+ for var in gmd.VALID_VARIABLES:
+ assert var in df['Variable'].values
+
+
+def test_list_countries():
+ df = gmd.list_countries()
+ assert isinstance(df, pd.DataFrame)
+ assert not df.empty
+
def test_gmd_default():
"""Test default gmd call"""
- df = gmd()
+ df = gmd.get_data()
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
assert all(col in df.columns for col in ["ISO3", "countryname", "year"])
+
def test_gmd_version():
"""Test gmd with specific version"""
- version = get_current_version()
- df = gmd(version=version)
+ version = gmd.get_current_version()
+ df = gmd.get_data(version=version)
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
+
def test_gmd_country():
"""Test gmd with specific country"""
- df = gmd(country="USA")
+ df = gmd.get_data(country="USA")
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
assert all(df["ISO3"] == "USA")
+
def test_gmd_countries():
"""Test gmd with multiple countries"""
- df = gmd(country=["USA", "CHN"])
+ df = gmd.get_data(country=["USA", "CHN"])
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
assert set(df["ISO3"].unique()) == {"USA", "CHN"}
+
def test_gmd_variables():
"""Test gmd with specific variables"""
- df = gmd(variables=["rGDP", "infl"])
+ df = gmd.get_data(variables=["rGDP", "infl"])
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
assert all(col in df.columns for col in ["rGDP", "infl"])
+
def test_gmd_raw():
"""Test gmd with raw data option"""
- df = gmd(variables="rGDP", raw=True)
+ df = gmd.get_data(variables="rGDP", raw=True)
assert isinstance(df, pd.DataFrame)
assert len(df) > 0
assert "rGDP" in df.columns
+
def test_gmd_combinations():
"""Test gmd with multiple parameters"""
- df = gmd(
- version=get_current_version(),
+ df = gmd.get_data(
+ version=gmd.get_current_version(),
country=["USA", "CHN"],
variables=["rGDP", "infl"]
)
@@ -92,27 +99,32 @@ def test_gmd_combinations():
assert set(df["ISO3"].unique()) == {"USA", "CHN"}
assert all(col in df.columns for col in ["rGDP", "infl"])
+
def test_gmd_invalid_version():
"""Test gmd with invalid version"""
- with pytest.raises(ValueError):
- gmd(version="invalid_version")
+ with pytest.raises(InvalidVersionError):
+ gmd.get_data(version="invalid_version")
+
def test_gmd_invalid_country():
"""Test gmd with invalid country"""
- with pytest.raises(ValueError):
- gmd(country="INVALID")
+ with pytest.raises(InvalidCountryError):
+ gmd.get_data(country="INVALID")
+
def test_gmd_invalid_variable():
"""Test gmd with invalid variable"""
- with pytest.raises(ValueError):
- gmd(variables="INVALID")
+ with pytest.raises(InvalidVariableError):
+ gmd.get_data(variables="INVALID")
+
def test_gmd_raw_multiple_variables():
"""Test gmd raw option with multiple variables"""
- with pytest.raises(ValueError):
- gmd(variables=["rGDP", "infl"], raw=True)
+ with pytest.raises(RawModeError):
+ gmd.get_data(variables=["rGDP", "infl"], raw=True)
+
def test_gmd_raw_no_variable():
"""Test gmd raw option without variable"""
- with pytest.raises(ValueError):
- gmd(raw=True)
\ No newline at end of file
+ with pytest.raises(RawModeError):
+ gmd.get_data(raw=True)
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..f71124a
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,69 @@
+[tox]
+envlist =
+ py38-pandas130-requests230
+ py39-pandas140-requests231
+ py310-pandas150-requests231
+ py310-pandas200-requests231
+ py310-pandas220-requests232
+ py312-pandas220-requests232
+skip_missing_interpreters = true
+
+[testenv]
+usedevelop = true
+deps =
+ pytest==8.2.1
+
+[testenv:py38-pandas130-requests230]
+basepython = python3.8
+deps =
+ {[testenv]deps}
+ numpy==1.18.0
+ pandas==1.0.5
+ requests==2.20.0
+ importlib_resources
+commands = pytest tests/
+
+[testenv:py39-pandas140-requests231]
+basepython = python3.9
+deps =
+ {[testenv]deps}
+ numpy==1.22.4
+ pandas==1.4.4
+ requests==2.31.0
+commands = pytest tests/
+
+[testenv:py310-pandas150-requests231]
+basepython = python3.10
+deps =
+ {[testenv]deps}
+ numpy==1.23.5
+ pandas==1.5.3
+ requests==2.31.0
+commands = pytest tests/
+
+[testenv:py310-pandas200-requests231]
+basepython = python3.10
+deps =
+ {[testenv]deps}
+ numpy==1.24.4
+ pandas==2.0.3
+ requests==2.31.0
+commands = pytest tests/
+
+[testenv:py310-pandas220-requests232]
+basepython = python3.10
+deps =
+ {[testenv]deps}
+ numpy==1.26.4
+ pandas==2.2.2
+ requests==2.32.4
+commands = pytest tests/
+
+[testenv:py312-pandas220-requests232]
+basepython = python3.12
+deps =
+ {[testenv]deps}
+ numpy==1.26.4
+ pandas==2.2.2
+ requests==2.32.4
+commands = pytest tests/