We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Remove six from install requires
# Description

At the moment [`six` is a required library](https://github.com/scikit-hep/pyhf/blob/c61b7e8da9d951d11e01a0f4e3e1c2a880521486/setup.py#L100). However, (as noted on [`six`'s PyPI page](https://pypi.org/project/six/1.14.0/))

> Six is a Python 2 and 3 compatibility library. It provides utility functions for smoothing over the differences between the Python versions with the goal of writing Python code that is compatible on both Python versions.

As pyhf is Python 3 only as of PR #704, `six` should no longer be used and shouldn't be required. At the moment its only use is in [`src/pyhf/modifiers/__init__.py`](https://github.com/scikit-hep/pyhf/blob/c61b7e8da9d951d11e01a0f4e3e1c2a880521486/src/pyhf/modifiers/__init__.py) to use `string_types`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `setup.py`
Content:
```
1 from setuptools import setup, find_packages
2 from os import path
3 
4 this_directory = path.abspath(path.dirname(__file__))
5 with open(path.join(this_directory, 'README.md'), encoding='utf-8') as readme_md:
6     long_description = readme_md.read()
7 
8 extras_require = {
9     'tensorflow': ['tensorflow~=2.0', 'tensorflow-probability~=0.8'],
10     'torch': ['torch~=1.2'],
11     'jax': ['jax~=0.1,>0.1.51', 'jaxlib~=0.1,>0.1.33'],
12     'xmlio': ['uproot'],
13     'minuit': ['iminuit'],
14 }
15 extras_require['backends'] = sorted(
16     set(
17         extras_require['tensorflow']
18         + extras_require['torch']
19         + extras_require['jax']
20         + extras_require['minuit']
21     )
22 )
23 extras_require['contrib'] = sorted(set(['matplotlib']))
24 
25 extras_require['test'] = sorted(
26     set(
27         extras_require['backends']
28         + extras_require['xmlio']
29         + extras_require['contrib']
30         + [
31             'pyflakes',
32             'pytest~=3.5',
33             'pytest-cov>=2.5.1',
34             'pytest-mock',
35             'pytest-benchmark[histogram]',
36             'pytest-console-scripts',
37             'pytest-mpl',
38             'pydocstyle',
39             'coverage>=4.0',  # coveralls
40             'papermill~=1.0',
41             'nteract-scrapbook~=0.2',
42             'check-manifest',
43             'matplotlib',
44             'jupyter',
45             'uproot~=3.3',
46             'graphviz',
47             'jsonpatch',
48             'black;python_version>="3.6"',  # Black is Python3 only
49         ]
50     )
51 )
52 extras_require['docs'] = sorted(
53     set(
54         [
55             'sphinx',
56             'sphinxcontrib-bibtex',
57             'sphinx-click',
58             'sphinx_rtd_theme',
59             'nbsphinx',
60             'ipywidgets',
61             'sphinx-issues',
62             'm2r',
63         ]
64     )
65 )
66 extras_require['develop'] = sorted(
67     set(
68         extras_require['docs']
69         + extras_require['test']
70         + ['nbdime', 'bumpversion', 'ipython', 'pre-commit', 'twine']
71     )
72 )
73 extras_require['complete'] = sorted(set(sum(extras_require.values(), [])))
74 
75 
76 setup(
77     name='pyhf',
78     version='0.4.1',
79     description='(partial) pure python histfactory implementation',
80     long_description=long_description,
81     long_description_content_type='text/markdown',
82     url='https://github.com/scikit-hep/pyhf',
83     author='Lukas Heinrich, Matthew Feickert, Giordon Stark',
84     author_email='lukas.heinrich@cern.ch, matthew.feickert@cern.ch, gstark@cern.ch',
85     license='Apache',
86     keywords='physics fitting numpy scipy tensorflow pytorch',
87     classifiers=[
88         "Programming Language :: Python :: 3",
89         "Programming Language :: Python :: 3.6",
90         "Programming Language :: Python :: 3.7",
91     ],
92     package_dir={'': 'src'},
93     packages=find_packages(where='src'),
94     include_package_data=True,
95     python_requires=">=3.6",
96     install_requires=[
97         'scipy',  # requires numpy, which is required by pyhf and tensorflow
98         'click>=6.0',  # for console scripts,
99         'tqdm',  # for readxml
100         'six',  # for modifiers
101         'jsonschema>=v3.0.0a2',  # for utils, alpha-release for draft 6
102         'jsonpatch',
103         'pyyaml',  # for parsing CLI equal-delimited options
104     ],
105     extras_require=extras_require,
106     entry_points={'console_scripts': ['pyhf=pyhf.cli:cli']},
107     dependency_links=[],
108     use_scm_version=lambda: {'local_scheme': lambda version: ''},
109 )
110
```

Path: `src/pyhf/modifiers/__init__.py`
Content:
```
1 from six import string_types
2 import logging
3 
4 from .. import exceptions
5 from .. import get_backend
6 
7 log = logging.getLogger(__name__)
8 
9 registry = {}
10 
11 
12 def validate_modifier_structure(modifier):
13     """
14     Check if given object contains the right structure for modifiers
15     """
16     required_methods = ['required_parset']
17 
18     for method in required_methods:
19         if not hasattr(modifier, method):
20             raise exceptions.InvalidModifier(
21                 'Expected {0:s} method on modifier {1:s}'.format(
22                     method, modifier.__name__
23                 )
24             )
25     return True
26 
27 
28 def add_to_registry(
29     cls, cls_name=None, constrained=False, pdf_type='normal', op_code='addition'
30 ):
31     """
32     Consistent add_to_registry() function that handles actually adding thing to the registry.
33 
34     Raises an error if the name to register for the modifier already exists in the registry,
35     or if the modifier does not have the right structure.
36     """
37     global registry
38     cls_name = cls_name or cls.__name__
39     if cls_name in registry:
40         raise KeyError('The modifier name "{0:s}" is already taken.'.format(cls_name))
41     # validate the structure
42     validate_modifier_structure(cls)
43     # set is_constrained
44     cls.is_constrained = constrained
45     if constrained:
46         tensorlib, _ = get_backend()
47         if not hasattr(tensorlib, pdf_type):
48             raise exceptions.InvalidModifier(
49                 'The specified pdf_type "{0:s}" is not valid for {1:s}({2:s}). See pyhf.tensor documentation for available pdfs.'.format(
50                     pdf_type, cls_name, cls.__name__
51                 )
52             )
53         cls.pdf_type = pdf_type
54     else:
55         cls.pdf_type = None
56 
57     if op_code not in ['addition', 'multiplication']:
58         raise exceptions.InvalidModifier(
59             'The specified op_code "{0:s}" is not valid for {1:s}({2:s}). See pyhf.modifier documentation for available operation codes.'.format(
60                 op_code, cls_name, cls.__name__
61             )
62         )
63     cls.op_code = op_code
64 
65     registry[cls_name] = cls
66 
67 
68 def modifier(*args, **kwargs):
69     """
70     Decorator for registering modifiers. To flag the modifier as a constrained modifier, add `constrained=True`.
71 
72 
73     Args:
74         name: the name of the modifier to use. Use the class name by default. (default: None)
75         constrained: whether the modifier is constrained or not. (default: False)
76         pdf_type: the name of the pdf to use from tensorlib if constrained. (default: normal)
77         op_code: the name of the operation the modifier performs on the data (e.g. addition, multiplication)
78 
79     Returns:
80         modifier
81 
82     Raises:
83         ValueError: too many keyword arguments, or too many arguments, or wrong arguments
84         TypeError: provided name is not a string
85         pyhf.exceptions.InvalidModifier: object does not have necessary modifier structure
86     """
87     #
88     # Examples:
89     #
90     #   >>> @modifiers.modifier
91     #   >>> ... class myCustomModifier(object):
92     #   >>> ...   @classmethod
93     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
94     #
95     #   >>> @modifiers.modifier(name='myCustomNamer')
96     #   >>> ... class myCustomModifier(object):
97     #   >>> ...   @classmethod
98     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
99     #
100     #   >>> @modifiers.modifier(constrained=False)
101     #   >>> ... class myUnconstrainedModifier(object):
102     #   >>> ...   @classmethod
103     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
104     #   >>> ...
105     #   >>> myUnconstrainedModifier.pdf_type
106     #   None
107     #
108     #   >>> @modifiers.modifier(constrained=True, pdf_type='poisson')
109     #   >>> ... class myConstrainedCustomPoissonModifier(object):
110     #   >>> ...   @classmethod
111     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
112     #   >>> ...
113     #   >>> myConstrainedCustomGaussianModifier.pdf_type
114     #   'poisson'
115     #
116     #   >>> @modifiers.modifier(constrained=True)
117     #   >>> ... class myCustomModifier(object):
118     #   >>> ...   @classmethod
119     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
120     #
121     #   >>> @modifiers.modifier(op_code='multiplication')
122     #   >>> ... class myMultiplierModifier(object):
123     #   >>> ...   @classmethod
124     #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass
125     #   >>> ...
126     #   >>> myMultiplierModifier.op_code
127     #   'multiplication'
128 
129     def _modifier(name, constrained, pdf_type, op_code):
130         def wrapper(cls):
131             add_to_registry(
132                 cls,
133                 cls_name=name,
134                 constrained=constrained,
135                 pdf_type=pdf_type,
136                 op_code=op_code,
137             )
138             return cls
139 
140         return wrapper
141 
142     name = kwargs.pop('name', None)
143     constrained = bool(kwargs.pop('constrained', False))
144     pdf_type = str(kwargs.pop('pdf_type', 'normal'))
145     op_code = str(kwargs.pop('op_code', 'addition'))
146     # check for unparsed keyword arguments
147     if kwargs:
148         raise ValueError('Unparsed keyword arguments {}'.format(kwargs.keys()))
149     # check to make sure the given name is a string, if passed in one
150     if not isinstance(name, string_types) and name is not None:
151         raise TypeError(
152             '@modifier must be given a string. You gave it {}'.format(type(name))
153         )
154 
155     if not args:
156         # called like @modifier(name='foo', constrained=False, pdf_type='normal', op_code='addition')
157         return _modifier(name, constrained, pdf_type, op_code)
158     elif len(args) == 1:
159         # called like @modifier
160         if not callable(args[0]):
161             raise ValueError('You must decorate a callable python object')
162         add_to_registry(
163             args[0],
164             cls_name=name,
165             constrained=constrained,
166             pdf_type=pdf_type,
167             op_code=op_code,
168         )
169         return args[0]
170     else:
171         raise ValueError(
172             '@modifier must be called with only keyword arguments, @modifier(name=\'foo\'), or no arguments, @modifier; ({0:d} given)'.format(
173                 len(args)
174             )
175         )
176 
177 
178 from .histosys import histosys, histosys_combined
179 from .lumi import lumi, lumi_combined
180 from .normfactor import normfactor, normfactor_combined
181 from .normsys import normsys, normsys_combined
182 from .shapefactor import shapefactor, shapefactor_combined
183 from .shapesys import shapesys, shapesys_combined
184 from .staterror import staterror, staterror_combined
185 
186 uncombined = {
187     'histosys': histosys,
188     'lumi': lumi,
189     'normfactor': normfactor,
190     'normsys': normsys,
191     'shapefactor': shapefactor,
192     'shapesys': shapesys,
193     'staterror': staterror,
194 }
195 
196 combined = {
197     'histosys': histosys_combined,
198     'lumi': lumi_combined,
199     'normfactor': normfactor_combined,
200     'normsys': normsys_combined,
201     'shapefactor': shapefactor_combined,
202     'shapesys': shapesys_combined,
203     'staterror': staterror_combined,
204 }
205 
206 __all__ = [
207     'histosys',
208     'histosys_combined',
209     'lumi',
210     'lumi_combined',
211     'normfactor',
212     'normfactor_combined',
213     'normsys',
214     'normsys_combined',
215     'shapefactor',
216     'shapefactor_combined',
217     'shapesys',
218     'shapesys_combined',
219     'staterror',
220     'staterror_combined',
221     'combined',
222 ]
223
```
--- 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/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -97,7 +97,6 @@
         'scipy',  # requires numpy, which is required by pyhf and tensorflow
         'click>=6.0',  # for console scripts,
         'tqdm',  # for readxml
-        'six',  # for modifiers
         'jsonschema>=v3.0.0a2',  # for utils, alpha-release for draft 6
         'jsonpatch',
         'pyyaml',  # for parsing CLI equal-delimited options
diff --git a/src/pyhf/modifiers/__init__.py b/src/pyhf/modifiers/__init__.py
--- a/src/pyhf/modifiers/__init__.py
+++ b/src/pyhf/modifiers/__init__.py
@@ -1,4 +1,3 @@
-from six import string_types
 import logging
 
 from .. import exceptions
@@ -147,7 +146,7 @@
     if kwargs:
         raise ValueError('Unparsed keyword arguments {}'.format(kwargs.keys()))
     # check to make sure the given name is a string, if passed in one
-    if not isinstance(name, string_types) and name is not None:
+    if not isinstance(name, str) and name is not None:
         raise TypeError(
             '@modifier must be given a string. You gave it {}'.format(type(name))
         )

{"golden_diff": "diff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -97,7 +97,6 @@\n         'scipy',  # requires numpy, which is required by pyhf and tensorflow\n         'click>=6.0',  # for console scripts,\n         'tqdm',  # for readxml\n-        'six',  # for modifiers\n         'jsonschema>=v3.0.0a2',  # for utils, alpha-release for draft 6\n         'jsonpatch',\n         'pyyaml',  # for parsing CLI equal-delimited options\ndiff --git a/src/pyhf/modifiers/__init__.py b/src/pyhf/modifiers/__init__.py\n--- a/src/pyhf/modifiers/__init__.py\n+++ b/src/pyhf/modifiers/__init__.py\n@@ -1,4 +1,3 @@\n-from six import string_types\n import logging\n \n from .. import exceptions\n@@ -147,7 +146,7 @@\n     if kwargs:\n         raise ValueError('Unparsed keyword arguments {}'.format(kwargs.keys()))\n     # check to make sure the given name is a string, if passed in one\n-    if not isinstance(name, string_types) and name is not None:\n+    if not isinstance(name, str) and name is not None:\n         raise TypeError(\n             '@modifier must be given a string. You gave it {}'.format(type(name))\n         )\n", "issue": "Remove six from install requires\n# Description\r\n\r\nAt the moment [`six` is a required library](https://github.com/scikit-hep/pyhf/blob/c61b7e8da9d951d11e01a0f4e3e1c2a880521486/setup.py#L100). However, (as noted on [`six`'s PyPI page](https://pypi.org/project/six/1.14.0/))\r\n\r\n> Six is a Python 2 and 3 compatibility library. It provides utility functions for smoothing over the differences between the Python versions with the goal of writing Python code that is compatible on both Python versions.\r\n\r\nAs pyhf is Python 3 only as of PR #704, `six` should no longer be used and shouldn't be required. At the moment its only use is in [`src/pyhf/modifiers/__init__.py`](https://github.com/scikit-hep/pyhf/blob/c61b7e8da9d951d11e01a0f4e3e1c2a880521486/src/pyhf/modifiers/__init__.py) to use `string_types`.\n", "before_files": [{"content": "from setuptools import setup, find_packages\nfrom os import path\n\nthis_directory = path.abspath(path.dirname(__file__))\nwith open(path.join(this_directory, 'README.md'), encoding='utf-8') as readme_md:\n    long_description = readme_md.read()\n\nextras_require = {\n    'tensorflow': ['tensorflow~=2.0', 'tensorflow-probability~=0.8'],\n    'torch': ['torch~=1.2'],\n    'jax': ['jax~=0.1,>0.1.51', 'jaxlib~=0.1,>0.1.33'],\n    'xmlio': ['uproot'],\n    'minuit': ['iminuit'],\n}\nextras_require['backends'] = sorted(\n    set(\n        extras_require['tensorflow']\n        + extras_require['torch']\n        + extras_require['jax']\n        + extras_require['minuit']\n    )\n)\nextras_require['contrib'] = sorted(set(['matplotlib']))\n\nextras_require['test'] = sorted(\n    set(\n        extras_require['backends']\n        + extras_require['xmlio']\n        + extras_require['contrib']\n        + [\n            'pyflakes',\n            'pytest~=3.5',\n            'pytest-cov>=2.5.1',\n            'pytest-mock',\n            'pytest-benchmark[histogram]',\n            'pytest-console-scripts',\n            'pytest-mpl',\n            'pydocstyle',\n            'coverage>=4.0',  # coveralls\n            'papermill~=1.0',\n            'nteract-scrapbook~=0.2',\n            'check-manifest',\n            'matplotlib',\n            'jupyter',\n            'uproot~=3.3',\n            'graphviz',\n            'jsonpatch',\n            'black;python_version>=\"3.6\"',  # Black is Python3 only\n        ]\n    )\n)\nextras_require['docs'] = sorted(\n    set(\n        [\n            'sphinx',\n            'sphinxcontrib-bibtex',\n            'sphinx-click',\n            'sphinx_rtd_theme',\n            'nbsphinx',\n            'ipywidgets',\n            'sphinx-issues',\n            'm2r',\n        ]\n    )\n)\nextras_require['develop'] = sorted(\n    set(\n        extras_require['docs']\n        + extras_require['test']\n        + ['nbdime', 'bumpversion', 'ipython', 'pre-commit', 'twine']\n    )\n)\nextras_require['complete'] = sorted(set(sum(extras_require.values(), [])))\n\n\nsetup(\n    name='pyhf',\n    version='0.4.1',\n    description='(partial) pure python histfactory implementation',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    url='https://github.com/scikit-hep/pyhf',\n    author='Lukas Heinrich, Matthew Feickert, Giordon Stark',\n    author_email='lukas.heinrich@cern.ch, matthew.feickert@cern.ch, gstark@cern.ch',\n    license='Apache',\n    keywords='physics fitting numpy scipy tensorflow pytorch',\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n    ],\n    package_dir={'': 'src'},\n    packages=find_packages(where='src'),\n    include_package_data=True,\n    python_requires=\">=3.6\",\n    install_requires=[\n        'scipy',  # requires numpy, which is required by pyhf and tensorflow\n        'click>=6.0',  # for console scripts,\n        'tqdm',  # for readxml\n        'six',  # for modifiers\n        'jsonschema>=v3.0.0a2',  # for utils, alpha-release for draft 6\n        'jsonpatch',\n        'pyyaml',  # for parsing CLI equal-delimited options\n    ],\n    extras_require=extras_require,\n    entry_points={'console_scripts': ['pyhf=pyhf.cli:cli']},\n    dependency_links=[],\n    use_scm_version=lambda: {'local_scheme': lambda version: ''},\n)\n", "path": "setup.py"}, {"content": "from six import string_types\nimport logging\n\nfrom .. import exceptions\nfrom .. import get_backend\n\nlog = logging.getLogger(__name__)\n\nregistry = {}\n\n\ndef validate_modifier_structure(modifier):\n    \"\"\"\n    Check if given object contains the right structure for modifiers\n    \"\"\"\n    required_methods = ['required_parset']\n\n    for method in required_methods:\n        if not hasattr(modifier, method):\n            raise exceptions.InvalidModifier(\n                'Expected {0:s} method on modifier {1:s}'.format(\n                    method, modifier.__name__\n                )\n            )\n    return True\n\n\ndef add_to_registry(\n    cls, cls_name=None, constrained=False, pdf_type='normal', op_code='addition'\n):\n    \"\"\"\n    Consistent add_to_registry() function that handles actually adding thing to the registry.\n\n    Raises an error if the name to register for the modifier already exists in the registry,\n    or if the modifier does not have the right structure.\n    \"\"\"\n    global registry\n    cls_name = cls_name or cls.__name__\n    if cls_name in registry:\n        raise KeyError('The modifier name \"{0:s}\" is already taken.'.format(cls_name))\n    # validate the structure\n    validate_modifier_structure(cls)\n    # set is_constrained\n    cls.is_constrained = constrained\n    if constrained:\n        tensorlib, _ = get_backend()\n        if not hasattr(tensorlib, pdf_type):\n            raise exceptions.InvalidModifier(\n                'The specified pdf_type \"{0:s}\" is not valid for {1:s}({2:s}). See pyhf.tensor documentation for available pdfs.'.format(\n                    pdf_type, cls_name, cls.__name__\n                )\n            )\n        cls.pdf_type = pdf_type\n    else:\n        cls.pdf_type = None\n\n    if op_code not in ['addition', 'multiplication']:\n        raise exceptions.InvalidModifier(\n            'The specified op_code \"{0:s}\" is not valid for {1:s}({2:s}). See pyhf.modifier documentation for available operation codes.'.format(\n                op_code, cls_name, cls.__name__\n            )\n        )\n    cls.op_code = op_code\n\n    registry[cls_name] = cls\n\n\ndef modifier(*args, **kwargs):\n    \"\"\"\n    Decorator for registering modifiers. To flag the modifier as a constrained modifier, add `constrained=True`.\n\n\n    Args:\n        name: the name of the modifier to use. Use the class name by default. (default: None)\n        constrained: whether the modifier is constrained or not. (default: False)\n        pdf_type: the name of the pdf to use from tensorlib if constrained. (default: normal)\n        op_code: the name of the operation the modifier performs on the data (e.g. addition, multiplication)\n\n    Returns:\n        modifier\n\n    Raises:\n        ValueError: too many keyword arguments, or too many arguments, or wrong arguments\n        TypeError: provided name is not a string\n        pyhf.exceptions.InvalidModifier: object does not have necessary modifier structure\n    \"\"\"\n    #\n    # Examples:\n    #\n    #   >>> @modifiers.modifier\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(name='myCustomNamer')\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(constrained=False)\n    #   >>> ... class myUnconstrainedModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myUnconstrainedModifier.pdf_type\n    #   None\n    #\n    #   >>> @modifiers.modifier(constrained=True, pdf_type='poisson')\n    #   >>> ... class myConstrainedCustomPoissonModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myConstrainedCustomGaussianModifier.pdf_type\n    #   'poisson'\n    #\n    #   >>> @modifiers.modifier(constrained=True)\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(op_code='multiplication')\n    #   >>> ... class myMultiplierModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myMultiplierModifier.op_code\n    #   'multiplication'\n\n    def _modifier(name, constrained, pdf_type, op_code):\n        def wrapper(cls):\n            add_to_registry(\n                cls,\n                cls_name=name,\n                constrained=constrained,\n                pdf_type=pdf_type,\n                op_code=op_code,\n            )\n            return cls\n\n        return wrapper\n\n    name = kwargs.pop('name', None)\n    constrained = bool(kwargs.pop('constrained', False))\n    pdf_type = str(kwargs.pop('pdf_type', 'normal'))\n    op_code = str(kwargs.pop('op_code', 'addition'))\n    # check for unparsed keyword arguments\n    if kwargs:\n        raise ValueError('Unparsed keyword arguments {}'.format(kwargs.keys()))\n    # check to make sure the given name is a string, if passed in one\n    if not isinstance(name, string_types) and name is not None:\n        raise TypeError(\n            '@modifier must be given a string. You gave it {}'.format(type(name))\n        )\n\n    if not args:\n        # called like @modifier(name='foo', constrained=False, pdf_type='normal', op_code='addition')\n        return _modifier(name, constrained, pdf_type, op_code)\n    elif len(args) == 1:\n        # called like @modifier\n        if not callable(args[0]):\n            raise ValueError('You must decorate a callable python object')\n        add_to_registry(\n            args[0],\n            cls_name=name,\n            constrained=constrained,\n            pdf_type=pdf_type,\n            op_code=op_code,\n        )\n        return args[0]\n    else:\n        raise ValueError(\n            '@modifier must be called with only keyword arguments, @modifier(name=\\'foo\\'), or no arguments, @modifier; ({0:d} given)'.format(\n                len(args)\n            )\n        )\n\n\nfrom .histosys import histosys, histosys_combined\nfrom .lumi import lumi, lumi_combined\nfrom .normfactor import normfactor, normfactor_combined\nfrom .normsys import normsys, normsys_combined\nfrom .shapefactor import shapefactor, shapefactor_combined\nfrom .shapesys import shapesys, shapesys_combined\nfrom .staterror import staterror, staterror_combined\n\nuncombined = {\n    'histosys': histosys,\n    'lumi': lumi,\n    'normfactor': normfactor,\n    'normsys': normsys,\n    'shapefactor': shapefactor,\n    'shapesys': shapesys,\n    'staterror': staterror,\n}\n\ncombined = {\n    'histosys': histosys_combined,\n    'lumi': lumi_combined,\n    'normfactor': normfactor_combined,\n    'normsys': normsys_combined,\n    'shapefactor': shapefactor_combined,\n    'shapesys': shapesys_combined,\n    'staterror': staterror_combined,\n}\n\n__all__ = [\n    'histosys',\n    'histosys_combined',\n    'lumi',\n    'lumi_combined',\n    'normfactor',\n    'normfactor_combined',\n    'normsys',\n    'normsys_combined',\n    'shapefactor',\n    'shapefactor_combined',\n    'shapesys',\n    'shapesys_combined',\n    'staterror',\n    'staterror_combined',\n    'combined',\n]\n", "path": "src/pyhf/modifiers/__init__.py"}], "after_files": [{"content": "from setuptools import setup, find_packages\nfrom os import path\n\nthis_directory = path.abspath(path.dirname(__file__))\nwith open(path.join(this_directory, 'README.md'), encoding='utf-8') as readme_md:\n    long_description = readme_md.read()\n\nextras_require = {\n    'tensorflow': ['tensorflow~=2.0', 'tensorflow-probability~=0.8'],\n    'torch': ['torch~=1.2'],\n    'jax': ['jax~=0.1,>0.1.51', 'jaxlib~=0.1,>0.1.33'],\n    'xmlio': ['uproot'],\n    'minuit': ['iminuit'],\n}\nextras_require['backends'] = sorted(\n    set(\n        extras_require['tensorflow']\n        + extras_require['torch']\n        + extras_require['jax']\n        + extras_require['minuit']\n    )\n)\nextras_require['contrib'] = sorted(set(['matplotlib']))\n\nextras_require['test'] = sorted(\n    set(\n        extras_require['backends']\n        + extras_require['xmlio']\n        + extras_require['contrib']\n        + [\n            'pyflakes',\n            'pytest~=3.5',\n            'pytest-cov>=2.5.1',\n            'pytest-mock',\n            'pytest-benchmark[histogram]',\n            'pytest-console-scripts',\n            'pytest-mpl',\n            'pydocstyle',\n            'coverage>=4.0',  # coveralls\n            'papermill~=1.0',\n            'nteract-scrapbook~=0.2',\n            'check-manifest',\n            'matplotlib',\n            'jupyter',\n            'uproot~=3.3',\n            'graphviz',\n            'jsonpatch',\n            'black;python_version>=\"3.6\"',  # Black is Python3 only\n        ]\n    )\n)\nextras_require['docs'] = sorted(\n    set(\n        [\n            'sphinx',\n            'sphinxcontrib-bibtex',\n            'sphinx-click',\n            'sphinx_rtd_theme',\n            'nbsphinx',\n            'ipywidgets',\n            'sphinx-issues',\n            'm2r',\n        ]\n    )\n)\nextras_require['develop'] = sorted(\n    set(\n        extras_require['docs']\n        + extras_require['test']\n        + ['nbdime', 'bumpversion', 'ipython', 'pre-commit', 'twine']\n    )\n)\nextras_require['complete'] = sorted(set(sum(extras_require.values(), [])))\n\n\nsetup(\n    name='pyhf',\n    version='0.4.1',\n    description='(partial) pure python histfactory implementation',\n    long_description=long_description,\n    long_description_content_type='text/markdown',\n    url='https://github.com/scikit-hep/pyhf',\n    author='Lukas Heinrich, Matthew Feickert, Giordon Stark',\n    author_email='lukas.heinrich@cern.ch, matthew.feickert@cern.ch, gstark@cern.ch',\n    license='Apache',\n    keywords='physics fitting numpy scipy tensorflow pytorch',\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.6\",\n        \"Programming Language :: Python :: 3.7\",\n    ],\n    package_dir={'': 'src'},\n    packages=find_packages(where='src'),\n    include_package_data=True,\n    python_requires=\">=3.6\",\n    install_requires=[\n        'scipy',  # requires numpy, which is required by pyhf and tensorflow\n        'click>=6.0',  # for console scripts,\n        'tqdm',  # for readxml\n        'jsonschema>=v3.0.0a2',  # for utils, alpha-release for draft 6\n        'jsonpatch',\n        'pyyaml',  # for parsing CLI equal-delimited options\n    ],\n    extras_require=extras_require,\n    entry_points={'console_scripts': ['pyhf=pyhf.cli:cli']},\n    dependency_links=[],\n    use_scm_version=lambda: {'local_scheme': lambda version: ''},\n)\n", "path": "setup.py"}, {"content": "import logging\n\nfrom .. import exceptions\nfrom .. import get_backend\n\nlog = logging.getLogger(__name__)\n\nregistry = {}\n\n\ndef validate_modifier_structure(modifier):\n    \"\"\"\n    Check if given object contains the right structure for modifiers\n    \"\"\"\n    required_methods = ['required_parset']\n\n    for method in required_methods:\n        if not hasattr(modifier, method):\n            raise exceptions.InvalidModifier(\n                'Expected {0:s} method on modifier {1:s}'.format(\n                    method, modifier.__name__\n                )\n            )\n    return True\n\n\ndef add_to_registry(\n    cls, cls_name=None, constrained=False, pdf_type='normal', op_code='addition'\n):\n    \"\"\"\n    Consistent add_to_registry() function that handles actually adding thing to the registry.\n\n    Raises an error if the name to register for the modifier already exists in the registry,\n    or if the modifier does not have the right structure.\n    \"\"\"\n    global registry\n    cls_name = cls_name or cls.__name__\n    if cls_name in registry:\n        raise KeyError('The modifier name \"{0:s}\" is already taken.'.format(cls_name))\n    # validate the structure\n    validate_modifier_structure(cls)\n    # set is_constrained\n    cls.is_constrained = constrained\n    if constrained:\n        tensorlib, _ = get_backend()\n        if not hasattr(tensorlib, pdf_type):\n            raise exceptions.InvalidModifier(\n                'The specified pdf_type \"{0:s}\" is not valid for {1:s}({2:s}). See pyhf.tensor documentation for available pdfs.'.format(\n                    pdf_type, cls_name, cls.__name__\n                )\n            )\n        cls.pdf_type = pdf_type\n    else:\n        cls.pdf_type = None\n\n    if op_code not in ['addition', 'multiplication']:\n        raise exceptions.InvalidModifier(\n            'The specified op_code \"{0:s}\" is not valid for {1:s}({2:s}). See pyhf.modifier documentation for available operation codes.'.format(\n                op_code, cls_name, cls.__name__\n            )\n        )\n    cls.op_code = op_code\n\n    registry[cls_name] = cls\n\n\ndef modifier(*args, **kwargs):\n    \"\"\"\n    Decorator for registering modifiers. To flag the modifier as a constrained modifier, add `constrained=True`.\n\n\n    Args:\n        name: the name of the modifier to use. Use the class name by default. (default: None)\n        constrained: whether the modifier is constrained or not. (default: False)\n        pdf_type: the name of the pdf to use from tensorlib if constrained. (default: normal)\n        op_code: the name of the operation the modifier performs on the data (e.g. addition, multiplication)\n\n    Returns:\n        modifier\n\n    Raises:\n        ValueError: too many keyword arguments, or too many arguments, or wrong arguments\n        TypeError: provided name is not a string\n        pyhf.exceptions.InvalidModifier: object does not have necessary modifier structure\n    \"\"\"\n    #\n    # Examples:\n    #\n    #   >>> @modifiers.modifier\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(name='myCustomNamer')\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(constrained=False)\n    #   >>> ... class myUnconstrainedModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myUnconstrainedModifier.pdf_type\n    #   None\n    #\n    #   >>> @modifiers.modifier(constrained=True, pdf_type='poisson')\n    #   >>> ... class myConstrainedCustomPoissonModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myConstrainedCustomGaussianModifier.pdf_type\n    #   'poisson'\n    #\n    #   >>> @modifiers.modifier(constrained=True)\n    #   >>> ... class myCustomModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #\n    #   >>> @modifiers.modifier(op_code='multiplication')\n    #   >>> ... class myMultiplierModifier(object):\n    #   >>> ...   @classmethod\n    #   >>> ...   def required_parset(cls, sample_data, modifier_data): pass\n    #   >>> ...\n    #   >>> myMultiplierModifier.op_code\n    #   'multiplication'\n\n    def _modifier(name, constrained, pdf_type, op_code):\n        def wrapper(cls):\n            add_to_registry(\n                cls,\n                cls_name=name,\n                constrained=constrained,\n                pdf_type=pdf_type,\n                op_code=op_code,\n            )\n            return cls\n\n        return wrapper\n\n    name = kwargs.pop('name', None)\n    constrained = bool(kwargs.pop('constrained', False))\n    pdf_type = str(kwargs.pop('pdf_type', 'normal'))\n    op_code = str(kwargs.pop('op_code', 'addition'))\n    # check for unparsed keyword arguments\n    if kwargs:\n        raise ValueError('Unparsed keyword arguments {}'.format(kwargs.keys()))\n    # check to make sure the given name is a string, if passed in one\n    if not isinstance(name, str) and name is not None:\n        raise TypeError(\n            '@modifier must be given a string. You gave it {}'.format(type(name))\n        )\n\n    if not args:\n        # called like @modifier(name='foo', constrained=False, pdf_type='normal', op_code='addition')\n        return _modifier(name, constrained, pdf_type, op_code)\n    elif len(args) == 1:\n        # called like @modifier\n        if not callable(args[0]):\n            raise ValueError('You must decorate a callable python object')\n        add_to_registry(\n            args[0],\n            cls_name=name,\n            constrained=constrained,\n            pdf_type=pdf_type,\n            op_code=op_code,\n        )\n        return args[0]\n    else:\n        raise ValueError(\n            '@modifier must be called with only keyword arguments, @modifier(name=\\'foo\\'), or no arguments, @modifier; ({0:d} given)'.format(\n                len(args)\n            )\n        )\n\n\nfrom .histosys import histosys, histosys_combined\nfrom .lumi import lumi, lumi_combined\nfrom .normfactor import normfactor, normfactor_combined\nfrom .normsys import normsys, normsys_combined\nfrom .shapefactor import shapefactor, shapefactor_combined\nfrom .shapesys import shapesys, shapesys_combined\nfrom .staterror import staterror, staterror_combined\n\nuncombined = {\n    'histosys': histosys,\n    'lumi': lumi,\n    'normfactor': normfactor,\n    'normsys': normsys,\n    'shapefactor': shapefactor,\n    'shapesys': shapesys,\n    'staterror': staterror,\n}\n\ncombined = {\n    'histosys': histosys_combined,\n    'lumi': lumi_combined,\n    'normfactor': normfactor_combined,\n    'normsys': normsys_combined,\n    'shapefactor': shapefactor_combined,\n    'shapesys': shapesys_combined,\n    'staterror': staterror_combined,\n}\n\n__all__ = [\n    'histosys',\n    'histosys_combined',\n    'lumi',\n    'lumi_combined',\n    'normfactor',\n    'normfactor_combined',\n    'normsys',\n    'normsys_combined',\n    'shapefactor',\n    'shapefactor_combined',\n    'shapesys',\n    'shapesys_combined',\n    'staterror',\n    'staterror_combined',\n    'combined',\n]\n", "path": "src/pyhf/modifiers/__init__.py"}]}