We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[Discussion] Dropping jinja2-time as dependency
* Cookiecutter version: 2.1.1
* Template project url: -
* Python version: 3.10
* Operating System: Void Linux

### Description:

I recently tried to package cookiecutter for Void Linux (https://github.com/void-linux/void-packages/pull/38176#issuecomment-1199183537), but was refused by the maintainers because of the jinja2-time dependency and its questionable state.

I'd like to ask if there's a solution on cookiecutter's side. I know it's a lot to ask to replace a dependency, but maybe this could benefit the project in general.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cookiecutter/extensions.py`
Content:
```
1 """Jinja2 extensions."""
2 import json
3 import string
4 import uuid
5 from secrets import choice
6 
7 from jinja2.ext import Extension
8 from slugify import slugify as pyslugify
9 
10 
11 class JsonifyExtension(Extension):
12     """Jinja2 extension to convert a Python object to JSON."""
13 
14     def __init__(self, environment):
15         """Initialize the extension with the given environment."""
16         super().__init__(environment)
17 
18         def jsonify(obj):
19             return json.dumps(obj, sort_keys=True, indent=4)
20 
21         environment.filters['jsonify'] = jsonify
22 
23 
24 class RandomStringExtension(Extension):
25     """Jinja2 extension to create a random string."""
26 
27     def __init__(self, environment):
28         """Jinja2 Extension Constructor."""
29         super().__init__(environment)
30 
31         def random_ascii_string(length, punctuation=False):
32             if punctuation:
33                 corpus = "".join((string.ascii_letters, string.punctuation))
34             else:
35                 corpus = string.ascii_letters
36             return "".join(choice(corpus) for _ in range(length))
37 
38         environment.globals.update(random_ascii_string=random_ascii_string)
39 
40 
41 class SlugifyExtension(Extension):
42     """Jinja2 Extension to slugify string."""
43 
44     def __init__(self, environment):
45         """Jinja2 Extension constructor."""
46         super().__init__(environment)
47 
48         def slugify(value, **kwargs):
49             """Slugifies the value."""
50             return pyslugify(value, **kwargs)
51 
52         environment.filters['slugify'] = slugify
53 
54 
55 class UUIDExtension(Extension):
56     """Jinja2 Extension to generate uuid4 string."""
57 
58     def __init__(self, environment):
59         """Jinja2 Extension constructor."""
60         super().__init__(environment)
61 
62         def uuid4():
63             """Generate UUID4."""
64             return str(uuid.uuid4())
65 
66         environment.globals.update(uuid4=uuid4)
67
```

Path: `cookiecutter/environment.py`
Content:
```
1 """Jinja2 environment and extensions loading."""
2 from jinja2 import Environment, StrictUndefined
3 
4 from cookiecutter.exceptions import UnknownExtension
5 
6 
7 class ExtensionLoaderMixin:
8     """Mixin providing sane loading of extensions specified in a given context.
9 
10     The context is being extracted from the keyword arguments before calling
11     the next parent class in line of the child.
12     """
13 
14     def __init__(self, **kwargs):
15         """Initialize the Jinja2 Environment object while loading extensions.
16 
17         Does the following:
18 
19         1. Establishes default_extensions (currently just a Time feature)
20         2. Reads extensions set in the cookiecutter.json _extensions key.
21         3. Attempts to load the extensions. Provides useful error if fails.
22         """
23         context = kwargs.pop('context', {})
24 
25         default_extensions = [
26             'cookiecutter.extensions.JsonifyExtension',
27             'cookiecutter.extensions.RandomStringExtension',
28             'cookiecutter.extensions.SlugifyExtension',
29             'cookiecutter.extensions.UUIDExtension',
30             'jinja2_time.TimeExtension',
31         ]
32         extensions = default_extensions + self._read_extensions(context)
33 
34         try:
35             super().__init__(extensions=extensions, **kwargs)
36         except ImportError as err:
37             raise UnknownExtension(f'Unable to load extension: {err}') from err
38 
39     def _read_extensions(self, context):
40         """Return list of extensions as str to be passed on to the Jinja2 env.
41 
42         If context does not contain the relevant info, return an empty
43         list instead.
44         """
45         try:
46             extensions = context['cookiecutter']['_extensions']
47         except KeyError:
48             return []
49         else:
50             return [str(ext) for ext in extensions]
51 
52 
53 class StrictEnvironment(ExtensionLoaderMixin, Environment):
54     """Create strict Jinja2 environment.
55 
56     Jinja2 environment will raise error on undefined variable in template-
57     rendering context.
58     """
59 
60     def __init__(self, **kwargs):
61         """Set the standard Cookiecutter StrictEnvironment.
62 
63         Also loading extensions defined in cookiecutter.json's _extensions key.
64         """
65         super().__init__(undefined=StrictUndefined, **kwargs)
66
```

Path: `setup.py`
Content:
```
1 """cookiecutter distutils configuration."""
2 from setuptools import setup
3 
4 version = "2.1.2.dev0"
5 
6 with open('README.md', encoding='utf-8') as readme_file:
7     readme = readme_file.read()
8 
9 requirements = [
10     'binaryornot>=0.4.4',
11     'Jinja2>=2.7,<4.0.0',
12     'click>=7.0,<9.0.0',
13     'pyyaml>=5.3.1',
14     'jinja2-time>=0.2.0',
15     'python-slugify>=4.0.0',
16     'requests>=2.23.0',
17 ]
18 
19 setup(
20     name='cookiecutter',
21     version=version,
22     description=(
23         'A command-line utility that creates projects from project '
24         'templates, e.g. creating a Python package project from a '
25         'Python package project template.'
26     ),
27     long_description=readme,
28     long_description_content_type='text/markdown',
29     author='Audrey Feldroy',
30     author_email='audreyr@gmail.com',
31     url='https://github.com/cookiecutter/cookiecutter',
32     project_urls={
33         "Documentation": "https://cookiecutter.readthedocs.io",
34         "Issues": "https://github.com/cookiecutter/cookiecutter/issues",
35         "Discord": "https://discord.gg/9BrxzPKuEW",
36     },
37     packages=['cookiecutter'],
38     package_dir={'cookiecutter': 'cookiecutter'},
39     entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']},
40     include_package_data=True,
41     python_requires='>=3.7',
42     install_requires=requirements,
43     license='BSD',
44     zip_safe=False,
45     classifiers=[
46         "Development Status :: 5 - Production/Stable",
47         "Environment :: Console",
48         "Intended Audience :: Developers",
49         "Natural Language :: English",
50         "License :: OSI Approved :: BSD License",
51         "Programming Language :: Python :: 3 :: Only",
52         "Programming Language :: Python :: 3",
53         "Programming Language :: Python :: 3.7",
54         "Programming Language :: Python :: 3.8",
55         "Programming Language :: Python :: 3.9",
56         "Programming Language :: Python :: 3.10",
57         "Programming Language :: Python :: 3.11",
58         "Programming Language :: Python :: Implementation :: CPython",
59         "Programming Language :: Python :: Implementation :: PyPy",
60         "Programming Language :: Python",
61         "Topic :: Software Development",
62     ],
63     keywords=[
64         "cookiecutter",
65         "Python",
66         "projects",
67         "project templates",
68         "Jinja2",
69         "skeleton",
70         "scaffolding",
71         "project directory",
72         "package",
73         "packaging",
74     ],
75 )
76
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/cookiecutter/environment.py b/cookiecutter/environment.py
--- a/cookiecutter/environment.py
+++ b/cookiecutter/environment.py
@@ -26,8 +26,8 @@
             'cookiecutter.extensions.JsonifyExtension',
             'cookiecutter.extensions.RandomStringExtension',
             'cookiecutter.extensions.SlugifyExtension',
+            'cookiecutter.extensions.TimeExtension',
             'cookiecutter.extensions.UUIDExtension',
-            'jinja2_time.TimeExtension',
         ]
         extensions = default_extensions + self._read_extensions(context)
 
diff --git a/cookiecutter/extensions.py b/cookiecutter/extensions.py
--- a/cookiecutter/extensions.py
+++ b/cookiecutter/extensions.py
@@ -1,9 +1,11 @@
 """Jinja2 extensions."""
+import arrow
 import json
 import string
 import uuid
 from secrets import choice
 
+from jinja2 import nodes
 from jinja2.ext import Extension
 from slugify import slugify as pyslugify
 
@@ -64,3 +66,65 @@
             return str(uuid.uuid4())
 
         environment.globals.update(uuid4=uuid4)
+
+
+class TimeExtension(Extension):
+    """Jinja2 Extension for dates and times."""
+
+    tags = {'now'}
+
+    def __init__(self, environment):
+        """Jinja2 Extension constructor."""
+        super().__init__(environment)
+
+        environment.extend(datetime_format='%Y-%m-%d')
+
+    def _datetime(self, timezone, operator, offset, datetime_format):
+        d = arrow.now(timezone)
+
+        # parse shift params from offset and include operator
+        shift_params = {}
+        for param in offset.split(','):
+            interval, value = param.split('=')
+            shift_params[interval.strip()] = float(operator + value.strip())
+        d = d.shift(**shift_params)
+
+        if datetime_format is None:
+            datetime_format = self.environment.datetime_format
+        return d.strftime(datetime_format)
+
+    def _now(self, timezone, datetime_format):
+        if datetime_format is None:
+            datetime_format = self.environment.datetime_format
+        return arrow.now(timezone).strftime(datetime_format)
+
+    def parse(self, parser):
+        """Parse datetime template and add datetime value."""
+        lineno = next(parser.stream).lineno
+
+        node = parser.parse_expression()
+
+        if parser.stream.skip_if('comma'):
+            datetime_format = parser.parse_expression()
+        else:
+            datetime_format = nodes.Const(None)
+
+        if isinstance(node, nodes.Add):
+            call_method = self.call_method(
+                '_datetime',
+                [node.left, nodes.Const('+'), node.right, datetime_format],
+                lineno=lineno,
+            )
+        elif isinstance(node, nodes.Sub):
+            call_method = self.call_method(
+                '_datetime',
+                [node.left, nodes.Const('-'), node.right, datetime_format],
+                lineno=lineno,
+            )
+        else:
+            call_method = self.call_method(
+                '_now',
+                [node, datetime_format],
+                lineno=lineno,
+            )
+        return nodes.Output([call_method], lineno=lineno)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -11,9 +11,9 @@
     'Jinja2>=2.7,<4.0.0',
     'click>=7.0,<9.0.0',
     'pyyaml>=5.3.1',
-    'jinja2-time>=0.2.0',
     'python-slugify>=4.0.0',
     'requests>=2.23.0',
+    'arrow',
 ]
 
 setup(

{"golden_diff": "diff --git a/cookiecutter/environment.py b/cookiecutter/environment.py\n--- a/cookiecutter/environment.py\n+++ b/cookiecutter/environment.py\n@@ -26,8 +26,8 @@\n             'cookiecutter.extensions.JsonifyExtension',\n             'cookiecutter.extensions.RandomStringExtension',\n             'cookiecutter.extensions.SlugifyExtension',\n+            'cookiecutter.extensions.TimeExtension',\n             'cookiecutter.extensions.UUIDExtension',\n-            'jinja2_time.TimeExtension',\n         ]\n         extensions = default_extensions + self._read_extensions(context)\n \ndiff --git a/cookiecutter/extensions.py b/cookiecutter/extensions.py\n--- a/cookiecutter/extensions.py\n+++ b/cookiecutter/extensions.py\n@@ -1,9 +1,11 @@\n \"\"\"Jinja2 extensions.\"\"\"\n+import arrow\n import json\n import string\n import uuid\n from secrets import choice\n \n+from jinja2 import nodes\n from jinja2.ext import Extension\n from slugify import slugify as pyslugify\n \n@@ -64,3 +66,65 @@\n             return str(uuid.uuid4())\n \n         environment.globals.update(uuid4=uuid4)\n+\n+\n+class TimeExtension(Extension):\n+    \"\"\"Jinja2 Extension for dates and times.\"\"\"\n+\n+    tags = {'now'}\n+\n+    def __init__(self, environment):\n+        \"\"\"Jinja2 Extension constructor.\"\"\"\n+        super().__init__(environment)\n+\n+        environment.extend(datetime_format='%Y-%m-%d')\n+\n+    def _datetime(self, timezone, operator, offset, datetime_format):\n+        d = arrow.now(timezone)\n+\n+        # parse shift params from offset and include operator\n+        shift_params = {}\n+        for param in offset.split(','):\n+            interval, value = param.split('=')\n+            shift_params[interval.strip()] = float(operator + value.strip())\n+        d = d.shift(**shift_params)\n+\n+        if datetime_format is None:\n+            datetime_format = self.environment.datetime_format\n+        return d.strftime(datetime_format)\n+\n+    def _now(self, timezone, datetime_format):\n+        if datetime_format is None:\n+            datetime_format = self.environment.datetime_format\n+        return arrow.now(timezone).strftime(datetime_format)\n+\n+    def parse(self, parser):\n+        \"\"\"Parse datetime template and add datetime value.\"\"\"\n+        lineno = next(parser.stream).lineno\n+\n+        node = parser.parse_expression()\n+\n+        if parser.stream.skip_if('comma'):\n+            datetime_format = parser.parse_expression()\n+        else:\n+            datetime_format = nodes.Const(None)\n+\n+        if isinstance(node, nodes.Add):\n+            call_method = self.call_method(\n+                '_datetime',\n+                [node.left, nodes.Const('+'), node.right, datetime_format],\n+                lineno=lineno,\n+            )\n+        elif isinstance(node, nodes.Sub):\n+            call_method = self.call_method(\n+                '_datetime',\n+                [node.left, nodes.Const('-'), node.right, datetime_format],\n+                lineno=lineno,\n+            )\n+        else:\n+            call_method = self.call_method(\n+                '_now',\n+                [node, datetime_format],\n+                lineno=lineno,\n+            )\n+        return nodes.Output([call_method], lineno=lineno)\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -11,9 +11,9 @@\n     'Jinja2>=2.7,<4.0.0',\n     'click>=7.0,<9.0.0',\n     'pyyaml>=5.3.1',\n-    'jinja2-time>=0.2.0',\n     'python-slugify>=4.0.0',\n     'requests>=2.23.0',\n+    'arrow',\n ]\n \n setup(\n", "issue": "[Discussion] Dropping jinja2-time as dependency\n* Cookiecutter version: 2.1.1\r\n* Template project url: -\r\n* Python version: 3.10\r\n* Operating System: Void Linux\r\n\r\n### Description:\r\n\r\nI recently tried to package cookiecutter for Void Linux (https://github.com/void-linux/void-packages/pull/38176#issuecomment-1199183537), but was refused by the maintainers because of the jinja2-time dependency and its questionable state.\r\n\r\nI'd like to ask if there's a solution on cookiecutter's side. I know it's a lot to ask to replace a dependency, but maybe this could benefit the project in general.\n", "before_files": [{"content": "\"\"\"Jinja2 extensions.\"\"\"\nimport json\nimport string\nimport uuid\nfrom secrets import choice\n\nfrom jinja2.ext import Extension\nfrom slugify import slugify as pyslugify\n\n\nclass JsonifyExtension(Extension):\n    \"\"\"Jinja2 extension to convert a Python object to JSON.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Initialize the extension with the given environment.\"\"\"\n        super().__init__(environment)\n\n        def jsonify(obj):\n            return json.dumps(obj, sort_keys=True, indent=4)\n\n        environment.filters['jsonify'] = jsonify\n\n\nclass RandomStringExtension(Extension):\n    \"\"\"Jinja2 extension to create a random string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension Constructor.\"\"\"\n        super().__init__(environment)\n\n        def random_ascii_string(length, punctuation=False):\n            if punctuation:\n                corpus = \"\".join((string.ascii_letters, string.punctuation))\n            else:\n                corpus = string.ascii_letters\n            return \"\".join(choice(corpus) for _ in range(length))\n\n        environment.globals.update(random_ascii_string=random_ascii_string)\n\n\nclass SlugifyExtension(Extension):\n    \"\"\"Jinja2 Extension to slugify string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension constructor.\"\"\"\n        super().__init__(environment)\n\n        def slugify(value, **kwargs):\n            \"\"\"Slugifies the value.\"\"\"\n            return pyslugify(value, **kwargs)\n\n        environment.filters['slugify'] = slugify\n\n\nclass UUIDExtension(Extension):\n    \"\"\"Jinja2 Extension to generate uuid4 string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension constructor.\"\"\"\n        super().__init__(environment)\n\n        def uuid4():\n            \"\"\"Generate UUID4.\"\"\"\n            return str(uuid.uuid4())\n\n        environment.globals.update(uuid4=uuid4)\n", "path": "cookiecutter/extensions.py"}, {"content": "\"\"\"Jinja2 environment and extensions loading.\"\"\"\nfrom jinja2 import Environment, StrictUndefined\n\nfrom cookiecutter.exceptions import UnknownExtension\n\n\nclass ExtensionLoaderMixin:\n    \"\"\"Mixin providing sane loading of extensions specified in a given context.\n\n    The context is being extracted from the keyword arguments before calling\n    the next parent class in line of the child.\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"Initialize the Jinja2 Environment object while loading extensions.\n\n        Does the following:\n\n        1. Establishes default_extensions (currently just a Time feature)\n        2. Reads extensions set in the cookiecutter.json _extensions key.\n        3. Attempts to load the extensions. Provides useful error if fails.\n        \"\"\"\n        context = kwargs.pop('context', {})\n\n        default_extensions = [\n            'cookiecutter.extensions.JsonifyExtension',\n            'cookiecutter.extensions.RandomStringExtension',\n            'cookiecutter.extensions.SlugifyExtension',\n            'cookiecutter.extensions.UUIDExtension',\n            'jinja2_time.TimeExtension',\n        ]\n        extensions = default_extensions + self._read_extensions(context)\n\n        try:\n            super().__init__(extensions=extensions, **kwargs)\n        except ImportError as err:\n            raise UnknownExtension(f'Unable to load extension: {err}') from err\n\n    def _read_extensions(self, context):\n        \"\"\"Return list of extensions as str to be passed on to the Jinja2 env.\n\n        If context does not contain the relevant info, return an empty\n        list instead.\n        \"\"\"\n        try:\n            extensions = context['cookiecutter']['_extensions']\n        except KeyError:\n            return []\n        else:\n            return [str(ext) for ext in extensions]\n\n\nclass StrictEnvironment(ExtensionLoaderMixin, Environment):\n    \"\"\"Create strict Jinja2 environment.\n\n    Jinja2 environment will raise error on undefined variable in template-\n    rendering context.\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"Set the standard Cookiecutter StrictEnvironment.\n\n        Also loading extensions defined in cookiecutter.json's _extensions key.\n        \"\"\"\n        super().__init__(undefined=StrictUndefined, **kwargs)\n", "path": "cookiecutter/environment.py"}, {"content": "\"\"\"cookiecutter distutils configuration.\"\"\"\nfrom setuptools import setup\n\nversion = \"2.1.2.dev0\"\n\nwith open('README.md', encoding='utf-8') as readme_file:\n    readme = readme_file.read()\n\nrequirements = [\n    'binaryornot>=0.4.4',\n    'Jinja2>=2.7,<4.0.0',\n    'click>=7.0,<9.0.0',\n    'pyyaml>=5.3.1',\n    'jinja2-time>=0.2.0',\n    'python-slugify>=4.0.0',\n    'requests>=2.23.0',\n]\n\nsetup(\n    name='cookiecutter',\n    version=version,\n    description=(\n        'A command-line utility that creates projects from project '\n        'templates, e.g. creating a Python package project from a '\n        'Python package project template.'\n    ),\n    long_description=readme,\n    long_description_content_type='text/markdown',\n    author='Audrey Feldroy',\n    author_email='audreyr@gmail.com',\n    url='https://github.com/cookiecutter/cookiecutter',\n    project_urls={\n        \"Documentation\": \"https://cookiecutter.readthedocs.io\",\n        \"Issues\": \"https://github.com/cookiecutter/cookiecutter/issues\",\n        \"Discord\": \"https://discord.gg/9BrxzPKuEW\",\n    },\n    packages=['cookiecutter'],\n    package_dir={'cookiecutter': 'cookiecutter'},\n    entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']},\n    include_package_data=True,\n    python_requires='>=3.7',\n    install_requires=requirements,\n    license='BSD',\n    zip_safe=False,\n    classifiers=[\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Console\",\n        \"Intended Audience :: Developers\",\n        \"Natural Language :: English\",\n        \"License :: OSI Approved :: BSD License\",\n        \"Programming Language :: Python :: 3 :: Only\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Programming Language :: Python :: 3.10\",\n        \"Programming Language :: Python :: 3.11\",\n        \"Programming Language :: Python :: Implementation :: CPython\",\n        \"Programming Language :: Python :: Implementation :: PyPy\",\n        \"Programming Language :: Python\",\n        \"Topic :: Software Development\",\n    ],\n    keywords=[\n        \"cookiecutter\",\n        \"Python\",\n        \"projects\",\n        \"project templates\",\n        \"Jinja2\",\n        \"skeleton\",\n        \"scaffolding\",\n        \"project directory\",\n        \"package\",\n        \"packaging\",\n    ],\n)\n", "path": "setup.py"}], "after_files": [{"content": "\"\"\"Jinja2 extensions.\"\"\"\nimport arrow\nimport json\nimport string\nimport uuid\nfrom secrets import choice\n\nfrom jinja2 import nodes\nfrom jinja2.ext import Extension\nfrom slugify import slugify as pyslugify\n\n\nclass JsonifyExtension(Extension):\n    \"\"\"Jinja2 extension to convert a Python object to JSON.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Initialize the extension with the given environment.\"\"\"\n        super().__init__(environment)\n\n        def jsonify(obj):\n            return json.dumps(obj, sort_keys=True, indent=4)\n\n        environment.filters['jsonify'] = jsonify\n\n\nclass RandomStringExtension(Extension):\n    \"\"\"Jinja2 extension to create a random string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension Constructor.\"\"\"\n        super().__init__(environment)\n\n        def random_ascii_string(length, punctuation=False):\n            if punctuation:\n                corpus = \"\".join((string.ascii_letters, string.punctuation))\n            else:\n                corpus = string.ascii_letters\n            return \"\".join(choice(corpus) for _ in range(length))\n\n        environment.globals.update(random_ascii_string=random_ascii_string)\n\n\nclass SlugifyExtension(Extension):\n    \"\"\"Jinja2 Extension to slugify string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension constructor.\"\"\"\n        super().__init__(environment)\n\n        def slugify(value, **kwargs):\n            \"\"\"Slugifies the value.\"\"\"\n            return pyslugify(value, **kwargs)\n\n        environment.filters['slugify'] = slugify\n\n\nclass UUIDExtension(Extension):\n    \"\"\"Jinja2 Extension to generate uuid4 string.\"\"\"\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension constructor.\"\"\"\n        super().__init__(environment)\n\n        def uuid4():\n            \"\"\"Generate UUID4.\"\"\"\n            return str(uuid.uuid4())\n\n        environment.globals.update(uuid4=uuid4)\n\n\nclass TimeExtension(Extension):\n    \"\"\"Jinja2 Extension for dates and times.\"\"\"\n\n    tags = {'now'}\n\n    def __init__(self, environment):\n        \"\"\"Jinja2 Extension constructor.\"\"\"\n        super().__init__(environment)\n\n        environment.extend(datetime_format='%Y-%m-%d')\n\n    def _datetime(self, timezone, operator, offset, datetime_format):\n        d = arrow.now(timezone)\n\n        # parse shift params from offset and include operator\n        shift_params = {}\n        for param in offset.split(','):\n            interval, value = param.split('=')\n            shift_params[interval.strip()] = float(operator + value.strip())\n        d = d.shift(**shift_params)\n\n        if datetime_format is None:\n            datetime_format = self.environment.datetime_format\n        return d.strftime(datetime_format)\n\n    def _now(self, timezone, datetime_format):\n        if datetime_format is None:\n            datetime_format = self.environment.datetime_format\n        return arrow.now(timezone).strftime(datetime_format)\n\n    def parse(self, parser):\n        \"\"\"Parse datetime template and add datetime value.\"\"\"\n        lineno = next(parser.stream).lineno\n\n        node = parser.parse_expression()\n\n        if parser.stream.skip_if('comma'):\n            datetime_format = parser.parse_expression()\n        else:\n            datetime_format = nodes.Const(None)\n\n        if isinstance(node, nodes.Add):\n            call_method = self.call_method(\n                '_datetime',\n                [node.left, nodes.Const('+'), node.right, datetime_format],\n                lineno=lineno,\n            )\n        elif isinstance(node, nodes.Sub):\n            call_method = self.call_method(\n                '_datetime',\n                [node.left, nodes.Const('-'), node.right, datetime_format],\n                lineno=lineno,\n            )\n        else:\n            call_method = self.call_method(\n                '_now',\n                [node, datetime_format],\n                lineno=lineno,\n            )\n        return nodes.Output([call_method], lineno=lineno)\n", "path": "cookiecutter/extensions.py"}, {"content": "\"\"\"Jinja2 environment and extensions loading.\"\"\"\nfrom jinja2 import Environment, StrictUndefined\n\nfrom cookiecutter.exceptions import UnknownExtension\n\n\nclass ExtensionLoaderMixin:\n    \"\"\"Mixin providing sane loading of extensions specified in a given context.\n\n    The context is being extracted from the keyword arguments before calling\n    the next parent class in line of the child.\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"Initialize the Jinja2 Environment object while loading extensions.\n\n        Does the following:\n\n        1. Establishes default_extensions (currently just a Time feature)\n        2. Reads extensions set in the cookiecutter.json _extensions key.\n        3. Attempts to load the extensions. Provides useful error if fails.\n        \"\"\"\n        context = kwargs.pop('context', {})\n\n        default_extensions = [\n            'cookiecutter.extensions.JsonifyExtension',\n            'cookiecutter.extensions.RandomStringExtension',\n            'cookiecutter.extensions.SlugifyExtension',\n            'cookiecutter.extensions.TimeExtension',\n            'cookiecutter.extensions.UUIDExtension',\n        ]\n        extensions = default_extensions + self._read_extensions(context)\n\n        try:\n            super().__init__(extensions=extensions, **kwargs)\n        except ImportError as err:\n            raise UnknownExtension(f'Unable to load extension: {err}') from err\n\n    def _read_extensions(self, context):\n        \"\"\"Return list of extensions as str to be passed on to the Jinja2 env.\n\n        If context does not contain the relevant info, return an empty\n        list instead.\n        \"\"\"\n        try:\n            extensions = context['cookiecutter']['_extensions']\n        except KeyError:\n            return []\n        else:\n            return [str(ext) for ext in extensions]\n\n\nclass StrictEnvironment(ExtensionLoaderMixin, Environment):\n    \"\"\"Create strict Jinja2 environment.\n\n    Jinja2 environment will raise error on undefined variable in template-\n    rendering context.\n    \"\"\"\n\n    def __init__(self, **kwargs):\n        \"\"\"Set the standard Cookiecutter StrictEnvironment.\n\n        Also loading extensions defined in cookiecutter.json's _extensions key.\n        \"\"\"\n        super().__init__(undefined=StrictUndefined, **kwargs)\n", "path": "cookiecutter/environment.py"}, {"content": "\"\"\"cookiecutter distutils configuration.\"\"\"\nfrom setuptools import setup\n\nversion = \"2.1.2.dev0\"\n\nwith open('README.md', encoding='utf-8') as readme_file:\n    readme = readme_file.read()\n\nrequirements = [\n    'binaryornot>=0.4.4',\n    'Jinja2>=2.7,<4.0.0',\n    'click>=7.0,<9.0.0',\n    'pyyaml>=5.3.1',\n    'python-slugify>=4.0.0',\n    'requests>=2.23.0',\n    'arrow',\n]\n\nsetup(\n    name='cookiecutter',\n    version=version,\n    description=(\n        'A command-line utility that creates projects from project '\n        'templates, e.g. creating a Python package project from a '\n        'Python package project template.'\n    ),\n    long_description=readme,\n    long_description_content_type='text/markdown',\n    author='Audrey Feldroy',\n    author_email='audreyr@gmail.com',\n    url='https://github.com/cookiecutter/cookiecutter',\n    project_urls={\n        \"Documentation\": \"https://cookiecutter.readthedocs.io\",\n        \"Issues\": \"https://github.com/cookiecutter/cookiecutter/issues\",\n        \"Discord\": \"https://discord.gg/9BrxzPKuEW\",\n    },\n    packages=['cookiecutter'],\n    package_dir={'cookiecutter': 'cookiecutter'},\n    entry_points={'console_scripts': ['cookiecutter = cookiecutter.__main__:main']},\n    include_package_data=True,\n    python_requires='>=3.7',\n    install_requires=requirements,\n    license='BSD',\n    zip_safe=False,\n    classifiers=[\n        \"Development Status :: 5 - Production/Stable\",\n        \"Environment :: Console\",\n        \"Intended Audience :: Developers\",\n        \"Natural Language :: English\",\n        \"License :: OSI Approved :: BSD License\",\n        \"Programming Language :: Python :: 3 :: Only\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Programming Language :: Python :: 3.10\",\n        \"Programming Language :: Python :: 3.11\",\n        \"Programming Language :: Python :: Implementation :: CPython\",\n        \"Programming Language :: Python :: Implementation :: PyPy\",\n        \"Programming Language :: Python\",\n        \"Topic :: Software Development\",\n    ],\n    keywords=[\n        \"cookiecutter\",\n        \"Python\",\n        \"projects\",\n        \"project templates\",\n        \"Jinja2\",\n        \"skeleton\",\n        \"scaffolding\",\n        \"project directory\",\n        \"package\",\n        \"packaging\",\n    ],\n)\n", "path": "setup.py"}]}