Skip to content

Commit

Permalink
ENH: Improve pathlib.Path support
Browse files Browse the repository at this point in the history
  • Loading branch information
snowman2 committed Sep 3, 2020
1 parent 9389f9d commit 414fbd1
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 147 deletions.
1 change: 1 addition & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Change Log
* BUG: Fix handling of polygon holes when calculating area in Geod (pull #686)
* ENH: Added :ref:`network` (#675, #691, #695)
* ENH: Added support for debugging internal PROJ (pull #696)
* ENH: Added pathlib support for data directory methods (pull #702)

2.6.1
~~~~~
Expand Down
35 changes: 17 additions & 18 deletions pyproj/datadir.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import os
import sys
from distutils.spawn import find_executable
from pathlib import Path
from typing import Union

from pyproj._datadir import ( # noqa: F401
_global_context_set_data_dir,
Expand All @@ -15,18 +17,18 @@
_VALIDATED_PROJ_DATA = None


def set_data_dir(proj_data_dir: str) -> None:
def set_data_dir(proj_data_dir: Union[str, Path]) -> None:
"""
Set the data directory for PROJ to use.
Parameters
----------
proj_data_dir: str
proj_data_dir: Union[str, Path]
The path to the PROJ data directory.
"""
global _USER_PROJ_DATA
global _VALIDATED_PROJ_DATA
_USER_PROJ_DATA = proj_data_dir
_USER_PROJ_DATA = str(proj_data_dir)
# set to none to re-validate
_VALIDATED_PROJ_DATA = None
# need to reset the global PROJ context
Expand All @@ -35,16 +37,16 @@ def set_data_dir(proj_data_dir: str) -> None:
_global_context_set_data_dir()


def append_data_dir(proj_data_dir: str) -> None:
def append_data_dir(proj_data_dir: Union[str, Path]) -> None:
"""
Add an additional data directory for PROJ to use.
Parameters
----------
proj_data_dir: str
proj_data_dir: Union[str, Path]
The path to the PROJ data directory.
"""
set_data_dir(os.pathsep.join([get_data_dir(), proj_data_dir]))
set_data_dir(os.pathsep.join([get_data_dir(), str(proj_data_dir)]))


def get_data_dir() -> str:
Expand All @@ -68,15 +70,14 @@ def get_data_dir() -> str:
if _VALIDATED_PROJ_DATA is not None:
return _VALIDATED_PROJ_DATA
global _USER_PROJ_DATA
internal_datadir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "proj_dir", "share", "proj"
)
internal_datadir = Path(__file__).absolute().parent / "proj_dir" / "share" / "proj"
proj_lib_dirs = os.environ.get("PROJ_LIB", "")
prefix_datadir = os.path.join(sys.prefix, "share", "proj")
prefix_datadir = Path(sys.prefix, "share", "proj")

def valid_data_dir(potential_data_dir):
if potential_data_dir is not None and os.path.exists(
os.path.join(potential_data_dir, "proj.db")
if (
potential_data_dir is not None
and Path(potential_data_dir, "proj.db").exists()
):
return True
return False
Expand All @@ -92,19 +93,17 @@ def valid_data_dirs(potential_data_dirs):
if valid_data_dirs(_USER_PROJ_DATA):
_VALIDATED_PROJ_DATA = _USER_PROJ_DATA
elif valid_data_dir(internal_datadir):
_VALIDATED_PROJ_DATA = internal_datadir
_VALIDATED_PROJ_DATA = str(internal_datadir)
elif valid_data_dirs(proj_lib_dirs):
_VALIDATED_PROJ_DATA = proj_lib_dirs
elif valid_data_dir(prefix_datadir):
_VALIDATED_PROJ_DATA = prefix_datadir
_VALIDATED_PROJ_DATA = str(prefix_datadir)
else:
proj_exe = find_executable("proj")
if proj_exe is not None:
system_proj_dir = os.path.join(
os.path.dirname(os.path.dirname(proj_exe)), "share", "proj"
)
system_proj_dir = Path(proj_exe).parent.parent / "share" / "proj"
if valid_data_dir(system_proj_dir):
_VALIDATED_PROJ_DATA = system_proj_dir
_VALIDATED_PROJ_DATA = str(system_proj_dir)

if _VALIDATED_PROJ_DATA is None:
raise DataDirError(
Expand Down
3 changes: 1 addition & 2 deletions pyproj/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ def _load_grid_geojson(target_directory=None) -> Dict[str, Any]:
short_name="files.geojson",
directory=target_directory,
)
with open(local_path, encoding="utf-8") as gridf:
return json.load(gridf)
return json.loads(local_path.read_text(encoding="utf-8"))


def get_transform_grid_list(
Expand Down
69 changes: 38 additions & 31 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,25 @@
import subprocess
import sys
from distutils.spawn import find_executable
from glob import glob
from pathlib import Path
from typing import Dict, List, Optional

from pkg_resources import parse_version
from setuptools import Extension, setup

PROJ_MIN_VERSION = parse_version("7.2.0")
CURRENT_FILE_PATH = os.path.dirname(os.path.abspath(__file__))
BASE_INTERNAL_PROJ_DIR = "proj_dir"
INTERNAL_PROJ_DIR = os.path.join(CURRENT_FILE_PATH, "pyproj", BASE_INTERNAL_PROJ_DIR)
CURRENT_FILE_PATH = Path(__file__).absolute().parent
BASE_INTERNAL_PROJ_DIR = Path("proj_dir")
INTERNAL_PROJ_DIR = CURRENT_FILE_PATH / "pyproj" / BASE_INTERNAL_PROJ_DIR


def check_proj_version(proj_dir):
def check_proj_version(proj_dir: Path):
"""checks that the PROJ library meets the minimum version"""
proj = os.path.join(proj_dir, "bin", "proj")
proj_ver_bytes = subprocess.check_output(proj, stderr=subprocess.STDOUT)
proj_ver_bytes = (proj_ver_bytes.decode("ascii").split()[1]).strip(",")
proj = proj_dir / "bin" / "proj"
proj_ver_bytes = subprocess.check_output(
str(proj), stderr=subprocess.STDOUT
).decode("ascii")
proj_ver_bytes = (proj_ver_bytes.split()[1]).strip(",")
proj_version = parse_version(proj_ver_bytes)
if proj_version < PROJ_MIN_VERSION:
raise SystemExit(
Expand All @@ -29,15 +32,18 @@ def check_proj_version(proj_dir):
return proj_version


def get_proj_dir():
def get_proj_dir() -> Path:
"""
This function finds the base PROJ directory.
"""
proj_dir = os.environ.get("PROJ_DIR")
if proj_dir is None and os.path.exists(INTERNAL_PROJ_DIR):
proj_dir_environ = os.environ.get("PROJ_DIR")
proj_dir: Optional[Path] = None
if proj_dir_environ is not None:
proj_dir = Path(proj_dir_environ)
if proj_dir is None and INTERNAL_PROJ_DIR.exists():
proj_dir = INTERNAL_PROJ_DIR
print(f"Internally compiled directory being used {INTERNAL_PROJ_DIR}.")
elif proj_dir is None and not os.path.exists(INTERNAL_PROJ_DIR):
elif proj_dir is None and not INTERNAL_PROJ_DIR.exists():
proj = find_executable("proj", path=sys.prefix)
if proj is None:
proj = find_executable("proj")
Expand All @@ -47,8 +53,8 @@ def get_proj_dir():
"For more information see: "
"https://pyproj4.github.io/pyproj/stable/installation.html"
)
proj_dir = os.path.dirname(os.path.dirname(proj))
elif proj_dir is not None and os.path.exists(proj_dir):
proj_dir = Path(proj).parent.parent
elif proj_dir is not None and proj_dir.exists():
print("PROJ_DIR is set, using existing proj4 installation..\n")
else:
raise SystemExit(f"ERROR: Invalid path for PROJ_DIR {proj_dir}")
Expand All @@ -58,20 +64,20 @@ def get_proj_dir():
return proj_dir


def get_proj_libdirs(proj_dir):
def get_proj_libdirs(proj_dir: Path) -> List[str]:
"""
This function finds the library directories
"""
proj_libdir = os.environ.get("PROJ_LIBDIR")
libdirs = []
if proj_libdir is None:
libdir_search_paths = (
os.path.join(proj_dir, "lib"),
os.path.join(proj_dir, "lib64"),
proj_dir / "lib",
proj_dir / "lib64",
)
for libdir_search_path in libdir_search_paths:
if os.path.exists(libdir_search_path):
libdirs.append(libdir_search_path)
if libdir_search_path.exists():
libdirs.append(str(libdir_search_path))
if not libdirs:
raise SystemExit(
"ERROR: PROJ_LIBDIR dir not found. Please set PROJ_LIBDIR."
Expand All @@ -81,15 +87,15 @@ def get_proj_libdirs(proj_dir):
return libdirs


def get_proj_incdirs(proj_dir):
def get_proj_incdirs(proj_dir: Path) -> List[str]:
"""
This function finds the include directories
"""
proj_incdir = os.environ.get("PROJ_INCDIR")
incdirs = []
if proj_incdir is None:
if os.path.exists(os.path.join(proj_dir, "include")):
incdirs.append(os.path.join(proj_dir, "include"))
if (proj_dir / "include").exists():
incdirs.append(str(proj_dir / "include"))
else:
raise SystemExit(
"ERROR: PROJ_INCDIR dir not found. Please set PROJ_INCDIR."
Expand All @@ -114,16 +120,16 @@ def get_cythonize_options():
return cythonize_options


def get_libraries(libdirs):
def get_libraries(libdirs: List[str]) -> List[str]:
"""
This function gets the libraries to cythonize with
"""
libraries = ["proj"]
if os.name == "nt":
for libdir in libdirs:
projlib = glob(os.path.join(libdir, "proj*.lib"))
projlib = list(Path(libdir).glob("proj*.lib"))
if projlib:
libraries = [os.path.basename(projlib[0]).split(".lib")[0]]
libraries = [str(projlib[0].stem)]
break
return libraries

Expand Down Expand Up @@ -174,18 +180,19 @@ def get_extension_modules():
)


def get_package_data():
def get_package_data() -> Dict[str, List[str]]:
"""
This function retrieves the package data
"""
# setup package data
package_data = {"pyproj": ["*.pyi", "py.typed"]}
if os.environ.get("PROJ_WHEEL") is not None and os.path.exists(INTERNAL_PROJ_DIR):
if os.environ.get("PROJ_WHEEL") is not None and INTERNAL_PROJ_DIR.exists():
package_data["pyproj"].append(
os.path.join(BASE_INTERNAL_PROJ_DIR, "share", "proj", "*")
str(BASE_INTERNAL_PROJ_DIR / "share" / "proj" / "*")
)
if os.environ.get("PROJ_WHEEL") is not None and os.path.exists(
os.path.join(CURRENT_FILE_PATH, "pyproj", ".lib")
if (
os.environ.get("PROJ_WHEEL") is not None
and (CURRENT_FILE_PATH / "pyproj" / ".lib").exists()
):
package_data["pyproj"].append(os.path.join(".lib", "*"))
return package_data
Expand All @@ -195,7 +202,7 @@ def get_version():
"""
retreive pyproj version information (taken from Fiona)
"""
with open(os.path.join("pyproj", "__init__.py"), "r") as f:
with open(Path("pyproj", "__init__.py"), "r") as f:
for line in f:
if line.find("__version__") >= 0:
# parse __version__ and remove surrounding " or '
Expand Down
Loading

0 comments on commit 414fbd1

Please sign in to comment.