prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  on windows, setup_py_python_requires attempts to open utf-8 setup.py as Windows-1252 and fails
  ### Description
  
  This [setup.py file](https://github.com/fgregg/fastcluster/blob/master/setup.py) is valid utf-8, and has a few non-ascii characters. In a windows build, `setup_py_python_requires` appears to be opening this file as if it was encoded like Windows-1252 and thus fails on some non-ascii characters.
  
  ### Build log
  
  https://github.com/fgregg/fastcluster/runs/4660766954?check_suite_focus=true#step:5:40
  
  ### CI config
  
  https://github.com/fgregg/fastcluster/blob/master/.github/workflows/pythonpackage.yml#L41-L47
  
  </issue>
  <code>
  [start of cibuildwheel/projectfiles.py]
  1 import ast
  2 import sys
  3 from configparser import ConfigParser
  4 from pathlib import Path
  5 from typing import Any, Optional
  6 
  7 import tomli
  8 
  9 if sys.version_info < (3, 8):
  10     Constant = ast.Str
  11 
  12     def get_constant(x: ast.Str) -> str:
  13         return x.s
  14 
  15 else:
  16     Constant = ast.Constant
  17 
  18     def get_constant(x: ast.Constant) -> Any:
  19         return x.value
  20 
  21 
  22 class Analyzer(ast.NodeVisitor):
  23     def __init__(self) -> None:
  24         self.requires_python: Optional[str] = None
  25 
  26     def visit(self, content: ast.AST) -> None:
  27         for node in ast.walk(content):
  28             for child in ast.iter_child_nodes(node):
  29                 child.parent = node  # type: ignore[attr-defined]
  30         super().visit(content)
  31 
  32     def visit_keyword(self, node: ast.keyword) -> None:
  33         self.generic_visit(node)
  34         if node.arg == "python_requires":
  35             # Must not be nested in an if or other structure
  36             # This will be Module -> Expr -> Call -> keyword
  37             if not hasattr(node.parent.parent.parent, "parent") and isinstance(  # type: ignore[attr-defined]
  38                 node.value, Constant
  39             ):
  40                 self.requires_python = get_constant(node.value)
  41 
  42 
  43 def setup_py_python_requires(content: str) -> Optional[str]:
  44     try:
  45         tree = ast.parse(content)
  46         analyzer = Analyzer()
  47         analyzer.visit(tree)
  48         return analyzer.requires_python or None
  49     except Exception:
  50         return None
  51 
  52 
  53 def get_requires_python_str(package_dir: Path) -> Optional[str]:
  54     """Return the python requires string from the most canonical source available, or None"""
  55 
  56     # Read in from pyproject.toml:project.requires-python
  57     try:
  58         with (package_dir / "pyproject.toml").open("rb") as f1:
  59             info = tomli.load(f1)
  60         return str(info["project"]["requires-python"])
  61     except (FileNotFoundError, KeyError, IndexError, TypeError):
  62         pass
  63 
  64     # Read in from setup.cfg:options.python_requires
  65     try:
  66         config = ConfigParser()
  67         config.read(package_dir / "setup.cfg")
  68         return str(config["options"]["python_requires"])
  69     except (FileNotFoundError, KeyError, IndexError, TypeError):
  70         pass
  71 
  72     try:
  73         with (package_dir / "setup.py").open() as f2:
  74             return setup_py_python_requires(f2.read())
  75     except FileNotFoundError:
  76         pass
  77 
  78     return None
  79
  [end of cibuildwheel/projectfiles.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py
  --- a/cibuildwheel/projectfiles.py
  +++ b/cibuildwheel/projectfiles.py
  @@ -70,7 +70,7 @@
           pass
   
       try:
  -        with (package_dir / "setup.py").open() as f2:
  +        with (package_dir / "setup.py").open(encoding="utf8") as f2:
               return setup_py_python_requires(f2.read())
       except FileNotFoundError:
           pass
  
verification_info: {"golden_diff": "diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py\n--- a/cibuildwheel/projectfiles.py\n+++ b/cibuildwheel/projectfiles.py\n@@ -70,7 +70,7 @@\n         pass\n \n     try:\n-        with (package_dir / \"setup.py\").open() as f2:\n+        with (package_dir / \"setup.py\").open(encoding=\"utf8\") as f2:\n             return setup_py_python_requires(f2.read())\n     except FileNotFoundError:\n         pass\n", "issue": "on windows, setup_py_python_requires attempts to open utf-8 setup.py as Windows-1252 and fails\n### Description\r\n\r\nThis [setup.py file](https://github.com/fgregg/fastcluster/blob/master/setup.py) is valid utf-8, and has a few non-ascii characters. In a windows build, `setup_py_python_requires` appears to be opening this file as if it was encoded like Windows-1252 and thus fails on some non-ascii characters.\r\n\r\n### Build log\r\n\r\nhttps://github.com/fgregg/fastcluster/runs/4660766954?check_suite_focus=true#step:5:40\r\n\r\n### CI config\r\n\r\nhttps://github.com/fgregg/fastcluster/blob/master/.github/workflows/pythonpackage.yml#L41-L47\n", "before_files": [{"content": "import ast\nimport sys\nfrom configparser import ConfigParser\nfrom pathlib import Path\nfrom typing import Any, Optional\n\nimport tomli\n\nif sys.version_info < (3, 8):\n    Constant = ast.Str\n\n    def get_constant(x: ast.Str) -> str:\n        return x.s\n\nelse:\n    Constant = ast.Constant\n\n    def get_constant(x: ast.Constant) -> Any:\n        return x.value\n\n\nclass Analyzer(ast.NodeVisitor):\n    def __init__(self) -> None:\n        self.requires_python: Optional[str] = None\n\n    def visit(self, content: ast.AST) -> None:\n        for node in ast.walk(content):\n            for child in ast.iter_child_nodes(node):\n                child.parent = node  # type: ignore[attr-defined]\n        super().visit(content)\n\n    def visit_keyword(self, node: ast.keyword) -> None:\n        self.generic_visit(node)\n        if node.arg == \"python_requires\":\n            # Must not be nested in an if or other structure\n            # This will be Module -> Expr -> Call -> keyword\n            if not hasattr(node.parent.parent.parent, \"parent\") and isinstance(  # type: ignore[attr-defined]\n                node.value, Constant\n            ):\n                self.requires_python = get_constant(node.value)\n\n\ndef setup_py_python_requires(content: str) -> Optional[str]:\n    try:\n        tree = ast.parse(content)\n        analyzer = Analyzer()\n        analyzer.visit(tree)\n        return analyzer.requires_python or None\n    except Exception:\n        return None\n\n\ndef get_requires_python_str(package_dir: Path) -> Optional[str]:\n    \"\"\"Return the python requires string from the most canonical source available, or None\"\"\"\n\n    # Read in from pyproject.toml:project.requires-python\n    try:\n        with (package_dir / \"pyproject.toml\").open(\"rb\") as f1:\n            info = tomli.load(f1)\n        return str(info[\"project\"][\"requires-python\"])\n    except (FileNotFoundError, KeyError, IndexError, TypeError):\n        pass\n\n    # Read in from setup.cfg:options.python_requires\n    try:\n        config = ConfigParser()\n        config.read(package_dir / \"setup.cfg\")\n        return str(config[\"options\"][\"python_requires\"])\n    except (FileNotFoundError, KeyError, IndexError, TypeError):\n        pass\n\n    try:\n        with (package_dir / \"setup.py\").open() as f2:\n            return setup_py_python_requires(f2.read())\n    except FileNotFoundError:\n        pass\n\n    return None\n", "path": "cibuildwheel/projectfiles.py"}]}