Repository: pyproj4/pyproj (language: python)

Issue:
Upcoming Cython 3.1 str <-> bytes errors
Building with the latest Cython nightly wheel causes errors with strings that are expected to be bytes and vice-versa. I'm not sure if this is due to an implicit cast that was removed somewhere or something else. My guess is this may be related to: https://github.com/cython/cython/pull/6374

```bash
pip install --upgrade --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple cython numpy
pip install .
python -m pytest
```

#### Problem description

```
____________________________________________________ ERROR collecting test/crs/test_crs.py ____________________________________________________
test/crs/test_crs.py:1670: in <module>
    CRS("EPSG:4979"),  # native 3D
pyproj/crs/crs.py:350: in __init__
    self._local.crs = _CRS(self.srs)
pyproj/_crs.pyx:2355: in pyproj._crs._CRS.__init__
    def __init__(self, const char *proj_string):
E   TypeError: expected bytes, str found
__________________________________________________ ERROR collecting test/test_datum_shift.py __________________________________________________
test/test_datum_shift.py:30: in <module>
    WGS84_PROJ = Proj(proj="latlong", datum="WGS84")
pyproj/proj.py:110: in __init__
    self.crs = CRS.from_user_input(projparams, **kwargs)
pyproj/crs/crs.py:503: in from_user_input
    return cls(value, **kwargs)
pyproj/crs/crs.py:350: in __init__
    self._local.crs = _CRS(self.srs)
pyproj/_crs.pyx:2355: in pyproj._crs._CRS.__init__
    def __init__(self, const char *proj_string):
E   TypeError: expected bytes, str found
```

#### Expected Output

No TypeErrors

#### Installation method

from source


Discussion / hints:
Thanks for the report. Probably just need to wrap it in the [cstrencode](https://github.com/pyproj4/pyproj/blob/f00ae91318a3ef89b25abf7e14ae59b1e9e00249/pyproj/_compat.pyx#L4) function.

Fix patch:
diff --git a/pyproj/_crs.pyx b/pyproj/_crs.pyx
index f8cfc63f..4e3f28c2 100644
--- a/pyproj/_crs.pyx
+++ b/pyproj/_crs.pyx
@@ -2352,13 +2352,13 @@ cdef class _CRS(Base):
         self._coordinate_operation = None
         self._type_name = None
 
-    def __init__(self, const char *proj_string):
+    def __init__(self, str proj_string):
         self.context = pyproj_context_create()
         self._context_manager = get_context_manager()
         # initialize projection
         self.projobj = proj_create(
             self.context,
-            proj_string,
+            cstrencode(proj_string),
         )
         if self.projobj == NULL:
             raise CRSError(f"Invalid projection: {proj_string}")
diff --git a/pyproj/_geod.pyx b/pyproj/_geod.pyx
index 70255170..364607ae 100644
--- a/pyproj/_geod.pyx
+++ b/pyproj/_geod.pyx
@@ -90,10 +90,7 @@ cdef class Geod:
         geod_init(&self._geod_geodesic, <double> a, <double> f)
         self.a = a
         self.f = f
-        # convert 'a' only for initstring
-        a_str = int(a) if a.is_integer() else a
-        f_str = int(f) if f.is_integer() else f
-        self.initstring = f"+a={a_str} +f={f_str}"
+        self.initstring = f"+{a=} +{f=}"
         self.sphere = sphere
         self.b = b
         self.es = es
diff --git a/pyproj/_transformer.pyx b/pyproj/_transformer.pyx
index 149e4d70..9464acf4 100644
--- a/pyproj/_transformer.pyx
+++ b/pyproj/_transformer.pyx
@@ -151,7 +151,8 @@ cdef class _TransformerGroup:
             double north_lat_degree
 
         if authority is not None:
-            c_authority = authority
+            tmp = cstrencode(authority)
+            c_authority = tmp
 
         try:
             operation_factory_context = proj_create_operation_factory_context(
diff --git a/pyproj/database.pyx b/pyproj/database.pyx
index befffe70..a4481de2 100644
--- a/pyproj/database.pyx
+++ b/pyproj/database.pyx
@@ -460,7 +460,7 @@ def get_database_metadata(str key not None):
     cdef const char* metadata = NULL
     metadata = proj_context_get_database_metadata(
         pyproj_context_create(),
-        cstrdecode(key),
+        cstrencode(key),
     )
     if metadata == NULL:
         return None


Test patch:
diff --git a/.github/workflows/test_proj_latest.yaml b/.github/workflows/test_proj_latest.yaml
index d9467b8e..2f04c2f9 100644
--- a/.github/workflows/test_proj_latest.yaml
+++ b/.github/workflows/test_proj_latest.yaml
@@ -1,4 +1,4 @@
-name: Test PROJ Latest
+name: Test PROJ and Cython Latest
 
 on:
   push:
@@ -41,7 +41,7 @@ jobs:
         shell: bash
         run: |
           python -V
-          python -m pip install cython
+          python -m pip install --upgrade --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple cython
           python -m pip install -e .
           python -m pip install -r requirements-test.txt
           pyproj -v
diff --git a/test/test_proj.py b/test/test_proj.py
index a933b955..68e9827c 100644
--- a/test/test_proj.py
+++ b/test/test_proj.py
@@ -232,7 +232,7 @@ def test_repr(self):
     def test_sphere(self):
         # ellipse is Venus 2000 (IAU2000:29900), which is a sphere
         g = Geod("+a=6051800 +b=6051800")
-        self.assertEqual(repr(g), "Geod('+a=6051800 +f=0')")
+        self.assertEqual(repr(g), "Geod('+a=6051800.0 +f=0.0')")
 
     # test __repr__ for Geod object
     def test_ellps_name_round_trip(self):