We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
pyhf commandline tools requires uproot extra
# Description

just issuing `pyhf --help` requires the uproot extra since `commandline.py` imports `writexml`.

# Expected Behavior

I don't need uproot if I don't want to use json2xml or xml2json

# Actual Behavior

I can't use `pyhf` without installing uproot.

# Steps to Reproduce

install master and run pyhf

# Checklist

- [x] Run `git fetch` to get the most up to date version of `master`
- [x] Searched through existing Issues to confirm this is not a duplicate issue
- [x] Filled out the Description, Expected Behavior, Actual Behavior, and Steps to Reproduce sections above or have edited/removed them in a way that fully describes the issue


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyhf/commandline.py`
Content:
```
1 import logging
2 
3 import click
4 import json
5 import os
6 
7 from . import writexml
8 from .utils import hypotest
9 from .pdf import Workspace
10 from .version import __version__
11 
12 logging.basicConfig()
13 log = logging.getLogger(__name__)
14 
15 # This is only needed for Python 2/3 compatibility
16 def ensure_dirs(path):
17     try:
18         os.makedirs(path, exist_ok=True)
19     except TypeError:
20         if not os.path.exists(path):
21             os.makedirs(path)
22 
23 
24 @click.group(context_settings=dict(help_option_names=['-h', '--help']))
25 @click.version_option(version=__version__)
26 def pyhf():
27     pass
28 
29 
30 @pyhf.command()
31 @click.argument('entrypoint-xml', type=click.Path(exists=True))
32 @click.option(
33     '--basedir',
34     help='The base directory for the XML files to point relative to.',
35     type=click.Path(exists=True),
36     default=os.getcwd(),
37 )
38 @click.option(
39     '--output-file',
40     help='The location of the output json file. If not specified, prints to screen.',
41     default=None,
42 )
43 @click.option('--track-progress/--hide-progress', default=True)
44 def xml2json(entrypoint_xml, basedir, output_file, track_progress):
45     """ Entrypoint XML: The top-level XML file for the PDF definition. """
46     try:
47         import uproot
48 
49         assert uproot
50     except ImportError:
51         log.error(
52             "xml2json requires uproot, please install pyhf using the "
53             "xmlio extra: pip install pyhf[xmlio] or install uproot "
54             "manually: pip install uproot"
55         )
56     from . import readxml
57 
58     spec = readxml.parse(entrypoint_xml, basedir, track_progress=track_progress)
59     if output_file is None:
60         print(json.dumps(spec, indent=4, sort_keys=True))
61     else:
62         with open(output_file, 'w+') as out_file:
63             json.dump(spec, out_file, indent=4, sort_keys=True)
64         log.debug("Written to {0:s}".format(output_file))
65 
66 
67 @pyhf.command()
68 @click.argument('workspace', default='-')
69 @click.option('--output-dir', type=click.Path(exists=True), default='.')
70 @click.option('--specroot', default='config')
71 @click.option('--dataroot', default='data')
72 @click.option('--resultprefix', default='FitConfig')
73 def json2xml(workspace, output_dir, specroot, dataroot, resultprefix):
74     try:
75         import uproot
76 
77         assert uproot
78     except ImportError:
79         log.error(
80             "json2xml requires uproot, please install pyhf using the "
81             "xmlio extra: pip install pyhf[xmlio] or install uproot "
82             "manually: pip install uproot"
83         )
84 
85     ensure_dirs(output_dir)
86     with click.open_file(workspace, 'r') as specstream:
87         d = json.load(specstream)
88         ensure_dirs(os.path.join(output_dir, specroot))
89         ensure_dirs(os.path.join(output_dir, dataroot))
90         with click.open_file(
91             os.path.join(output_dir, '{0:s}.xml'.format(resultprefix)), 'w'
92         ) as outstream:
93             outstream.write(
94                 writexml.writexml(
95                     d,
96                     os.path.join(output_dir, specroot),
97                     os.path.join(output_dir, dataroot),
98                     resultprefix,
99                 ).decode('utf-8')
100             )
101 
102 
103 @pyhf.command()
104 @click.argument('workspace', default='-')
105 @click.option(
106     '--output-file',
107     help='The location of the output json file. If not specified, prints to screen.',
108     default=None,
109 )
110 @click.option('--measurement', default=None)
111 @click.option('-p', '--patch', multiple=True)
112 @click.option('--testpoi', default=1.0)
113 def cls(workspace, output_file, measurement, patch, testpoi):
114     with click.open_file(workspace, 'r') as specstream:
115         wspec = json.load(specstream)
116 
117     w = Workspace(wspec)
118 
119     patches = [json.loads(click.open_file(pfile, 'r').read()) for pfile in patch]
120     p = w.model(measurement_name=measurement, patches=patches)
121     result = hypotest(testpoi, w.data(p), p, return_expected_set=True)
122     result = {'CLs_obs': result[0].tolist()[0], 'CLs_exp': result[-1].ravel().tolist()}
123     if output_file is None:
124         print(json.dumps(result, indent=4, sort_keys=True))
125     else:
126         with open(output_file, 'w+') as out_file:
127             json.dump(result, out_file, indent=4, sort_keys=True)
128         log.debug("Written to {0:s}".format(output_file))
129
```
--- 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/pyhf/commandline.py b/pyhf/commandline.py
--- a/pyhf/commandline.py
+++ b/pyhf/commandline.py
@@ -4,7 +4,6 @@
 import json
 import os
 
-from . import writexml
 from .utils import hypotest
 from .pdf import Workspace
 from .version import __version__
@@ -81,6 +80,7 @@
             "xmlio extra: pip install pyhf[xmlio] or install uproot "
             "manually: pip install uproot"
         )
+    from . import writexml
 
     ensure_dirs(output_dir)
     with click.open_file(workspace, 'r') as specstream:

{"golden_diff": "diff --git a/pyhf/commandline.py b/pyhf/commandline.py\n--- a/pyhf/commandline.py\n+++ b/pyhf/commandline.py\n@@ -4,7 +4,6 @@\n import json\n import os\n \n-from . import writexml\n from .utils import hypotest\n from .pdf import Workspace\n from .version import __version__\n@@ -81,6 +80,7 @@\n             \"xmlio extra: pip install pyhf[xmlio] or install uproot \"\n             \"manually: pip install uproot\"\n         )\n+    from . import writexml\n \n     ensure_dirs(output_dir)\n     with click.open_file(workspace, 'r') as specstream:\n", "issue": "pyhf commandline tools requires uproot extra\n# Description\r\n\r\njust issuing `pyhf --help` requires the uproot extra since `commandline.py` imports `writexml`.\r\n\r\n# Expected Behavior\r\n\r\nI don't need uproot if I don't want to use json2xml or xml2json\r\n\r\n# Actual Behavior\r\n\r\nI can't use `pyhf` without installing uproot.\r\n\r\n# Steps to Reproduce\r\n\r\ninstall master and run pyhf\r\n\r\n# Checklist\r\n\r\n- [x] Run `git fetch` to get the most up to date version of `master`\r\n- [x] Searched through existing Issues to confirm this is not a duplicate issue\r\n- [x] Filled out the Description, Expected Behavior, Actual Behavior, and Steps to Reproduce sections above or have edited/removed them in a way that fully describes the issue\r\n\n", "before_files": [{"content": "import logging\n\nimport click\nimport json\nimport os\n\nfrom . import writexml\nfrom .utils import hypotest\nfrom .pdf import Workspace\nfrom .version import __version__\n\nlogging.basicConfig()\nlog = logging.getLogger(__name__)\n\n# This is only needed for Python 2/3 compatibility\ndef ensure_dirs(path):\n    try:\n        os.makedirs(path, exist_ok=True)\n    except TypeError:\n        if not os.path.exists(path):\n            os.makedirs(path)\n\n\n@click.group(context_settings=dict(help_option_names=['-h', '--help']))\n@click.version_option(version=__version__)\ndef pyhf():\n    pass\n\n\n@pyhf.command()\n@click.argument('entrypoint-xml', type=click.Path(exists=True))\n@click.option(\n    '--basedir',\n    help='The base directory for the XML files to point relative to.',\n    type=click.Path(exists=True),\n    default=os.getcwd(),\n)\n@click.option(\n    '--output-file',\n    help='The location of the output json file. If not specified, prints to screen.',\n    default=None,\n)\n@click.option('--track-progress/--hide-progress', default=True)\ndef xml2json(entrypoint_xml, basedir, output_file, track_progress):\n    \"\"\" Entrypoint XML: The top-level XML file for the PDF definition. \"\"\"\n    try:\n        import uproot\n\n        assert uproot\n    except ImportError:\n        log.error(\n            \"xml2json requires uproot, please install pyhf using the \"\n            \"xmlio extra: pip install pyhf[xmlio] or install uproot \"\n            \"manually: pip install uproot\"\n        )\n    from . import readxml\n\n    spec = readxml.parse(entrypoint_xml, basedir, track_progress=track_progress)\n    if output_file is None:\n        print(json.dumps(spec, indent=4, sort_keys=True))\n    else:\n        with open(output_file, 'w+') as out_file:\n            json.dump(spec, out_file, indent=4, sort_keys=True)\n        log.debug(\"Written to {0:s}\".format(output_file))\n\n\n@pyhf.command()\n@click.argument('workspace', default='-')\n@click.option('--output-dir', type=click.Path(exists=True), default='.')\n@click.option('--specroot', default='config')\n@click.option('--dataroot', default='data')\n@click.option('--resultprefix', default='FitConfig')\ndef json2xml(workspace, output_dir, specroot, dataroot, resultprefix):\n    try:\n        import uproot\n\n        assert uproot\n    except ImportError:\n        log.error(\n            \"json2xml requires uproot, please install pyhf using the \"\n            \"xmlio extra: pip install pyhf[xmlio] or install uproot \"\n            \"manually: pip install uproot\"\n        )\n\n    ensure_dirs(output_dir)\n    with click.open_file(workspace, 'r') as specstream:\n        d = json.load(specstream)\n        ensure_dirs(os.path.join(output_dir, specroot))\n        ensure_dirs(os.path.join(output_dir, dataroot))\n        with click.open_file(\n            os.path.join(output_dir, '{0:s}.xml'.format(resultprefix)), 'w'\n        ) as outstream:\n            outstream.write(\n                writexml.writexml(\n                    d,\n                    os.path.join(output_dir, specroot),\n                    os.path.join(output_dir, dataroot),\n                    resultprefix,\n                ).decode('utf-8')\n            )\n\n\n@pyhf.command()\n@click.argument('workspace', default='-')\n@click.option(\n    '--output-file',\n    help='The location of the output json file. If not specified, prints to screen.',\n    default=None,\n)\n@click.option('--measurement', default=None)\n@click.option('-p', '--patch', multiple=True)\n@click.option('--testpoi', default=1.0)\ndef cls(workspace, output_file, measurement, patch, testpoi):\n    with click.open_file(workspace, 'r') as specstream:\n        wspec = json.load(specstream)\n\n    w = Workspace(wspec)\n\n    patches = [json.loads(click.open_file(pfile, 'r').read()) for pfile in patch]\n    p = w.model(measurement_name=measurement, patches=patches)\n    result = hypotest(testpoi, w.data(p), p, return_expected_set=True)\n    result = {'CLs_obs': result[0].tolist()[0], 'CLs_exp': result[-1].ravel().tolist()}\n    if output_file is None:\n        print(json.dumps(result, indent=4, sort_keys=True))\n    else:\n        with open(output_file, 'w+') as out_file:\n            json.dump(result, out_file, indent=4, sort_keys=True)\n        log.debug(\"Written to {0:s}\".format(output_file))\n", "path": "pyhf/commandline.py"}], "after_files": [{"content": "import logging\n\nimport click\nimport json\nimport os\n\nfrom .utils import hypotest\nfrom .pdf import Workspace\nfrom .version import __version__\n\nlogging.basicConfig()\nlog = logging.getLogger(__name__)\n\n# This is only needed for Python 2/3 compatibility\ndef ensure_dirs(path):\n    try:\n        os.makedirs(path, exist_ok=True)\n    except TypeError:\n        if not os.path.exists(path):\n            os.makedirs(path)\n\n\n@click.group(context_settings=dict(help_option_names=['-h', '--help']))\n@click.version_option(version=__version__)\ndef pyhf():\n    pass\n\n\n@pyhf.command()\n@click.argument('entrypoint-xml', type=click.Path(exists=True))\n@click.option(\n    '--basedir',\n    help='The base directory for the XML files to point relative to.',\n    type=click.Path(exists=True),\n    default=os.getcwd(),\n)\n@click.option(\n    '--output-file',\n    help='The location of the output json file. If not specified, prints to screen.',\n    default=None,\n)\n@click.option('--track-progress/--hide-progress', default=True)\ndef xml2json(entrypoint_xml, basedir, output_file, track_progress):\n    \"\"\" Entrypoint XML: The top-level XML file for the PDF definition. \"\"\"\n    try:\n        import uproot\n\n        assert uproot\n    except ImportError:\n        log.error(\n            \"xml2json requires uproot, please install pyhf using the \"\n            \"xmlio extra: pip install pyhf[xmlio] or install uproot \"\n            \"manually: pip install uproot\"\n        )\n    from . import readxml\n\n    spec = readxml.parse(entrypoint_xml, basedir, track_progress=track_progress)\n    if output_file is None:\n        print(json.dumps(spec, indent=4, sort_keys=True))\n    else:\n        with open(output_file, 'w+') as out_file:\n            json.dump(spec, out_file, indent=4, sort_keys=True)\n        log.debug(\"Written to {0:s}\".format(output_file))\n\n\n@pyhf.command()\n@click.argument('workspace', default='-')\n@click.option('--output-dir', type=click.Path(exists=True), default='.')\n@click.option('--specroot', default='config')\n@click.option('--dataroot', default='data')\n@click.option('--resultprefix', default='FitConfig')\ndef json2xml(workspace, output_dir, specroot, dataroot, resultprefix):\n    try:\n        import uproot\n\n        assert uproot\n    except ImportError:\n        log.error(\n            \"json2xml requires uproot, please install pyhf using the \"\n            \"xmlio extra: pip install pyhf[xmlio] or install uproot \"\n            \"manually: pip install uproot\"\n        )\n    from . import writexml\n\n    ensure_dirs(output_dir)\n    with click.open_file(workspace, 'r') as specstream:\n        d = json.load(specstream)\n        ensure_dirs(os.path.join(output_dir, specroot))\n        ensure_dirs(os.path.join(output_dir, dataroot))\n        with click.open_file(\n            os.path.join(output_dir, '{0:s}.xml'.format(resultprefix)), 'w'\n        ) as outstream:\n            outstream.write(\n                writexml.writexml(\n                    d,\n                    os.path.join(output_dir, specroot),\n                    os.path.join(output_dir, dataroot),\n                    resultprefix,\n                ).decode('utf-8')\n            )\n\n\n@pyhf.command()\n@click.argument('workspace', default='-')\n@click.option(\n    '--output-file',\n    help='The location of the output json file. If not specified, prints to screen.',\n    default=None,\n)\n@click.option('--measurement', default=None)\n@click.option('-p', '--patch', multiple=True)\n@click.option('--testpoi', default=1.0)\ndef cls(workspace, output_file, measurement, patch, testpoi):\n    with click.open_file(workspace, 'r') as specstream:\n        wspec = json.load(specstream)\n\n    w = Workspace(wspec)\n\n    patches = [json.loads(click.open_file(pfile, 'r').read()) for pfile in patch]\n    p = w.model(measurement_name=measurement, patches=patches)\n    result = hypotest(testpoi, w.data(p), p, return_expected_set=True)\n    result = {'CLs_obs': result[0].tolist()[0], 'CLs_exp': result[-1].ravel().tolist()}\n    if output_file is None:\n        print(json.dumps(result, indent=4, sort_keys=True))\n    else:\n        with open(output_file, 'w+') as out_file:\n            json.dump(result, out_file, indent=4, sort_keys=True)\n        log.debug(\"Written to {0:s}\".format(output_file))\n", "path": "pyhf/commandline.py"}]}