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) - - Website Badge - - -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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/