We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement a `predictor_from_json_ai` high level method
## Task

Lightwood aims at offering _declarative machine learning_ capabilities by enabling users to specify _what_ they want their models to learn, without necessarily having to look at the _how_.

A key ingredient for this feature is our intermediate representation called Json AI (a.k.a. _J{ai}son_). The objective behind Json AI is to build Lightwood predictors out of a JSON-like file.

We offer a variety of methods to cover the spectrum of `ProblemDefinition` <-> `Json AI` <-> `Lightwood Predictor`. However, we are currently missing a method to go _directly_ from `Json AI` to a lightwood predictor object. The current approach is:

```python
code = code_from_json_ai(json_ai)
predictor = predictor_from_code(code)
```

In this task, we ask for an implementation of a high level function so that the user is now able to do:

```python
predictor = predictor_from_json_ai(json_ai)
```

## Steps :male_detective: :female_detective: 

- Fork the Lightwood repository, checkout the `staging` branch and from it create a new one.
- Implement the function inside `lightwood/api/high_level.py`. Be sure to add type hints and a comprehensive docstring.
- Make the PR and address any comments that reviewers might make.

## Additional rewards :1st_place_medal: 

Each Lightwood API PR brings 3️⃣ points for entry into the draw for a :computer:  Deep Learning Laptop powered by the NVIDIA RTX 3080 Max-Q GPU or other swag :shirt: :bear: .  For more info check out https://mindsdb.com/hacktoberfest/

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lightwood/api/high_level.py`
Content:
```
1 import os
2 from types import ModuleType
3 from typing import Union
4 import dill
5 import pandas as pd
6 from lightwood.api.types import DataAnalysis, JsonAI, ProblemDefinition
7 import lightwood
8 from lightwood.api.predictor import PredictorInterface
9 from lightwood.api.json_ai import generate_json_ai
10 import tempfile
11 from lightwood.api.json_ai import code_from_json_ai as _code_from_json_ai
12 import importlib.util
13 import sys
14 import random
15 import string
16 import gc
17 import time
18 from lightwood.helpers.log import log
19 
20 
21 def predictor_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> PredictorInterface:
22     """
23     Creates a ready-to-train ``Predictor`` object from some raw data and a ``ProblemDefinition``. Do not use this if you want to edit the JsonAI first. Usually you'd want to next train this predictor by calling the ``learn`` method on the same dataframe used to create it.
24 
25     :param df: The raw data
26     :param problem_definition: The manual specifications for your predictive problem
27 
28     :returns: A lightwood ``Predictor`` object
29     """ # noqa
30     if not isinstance(problem_definition, ProblemDefinition):
31         problem_definition = ProblemDefinition.from_dict(problem_definition)
32 
33     log.info(f'Dropping features: {problem_definition.ignore_features}')
34     df = df.drop(columns=problem_definition.ignore_features)
35 
36     predictor_class_str = code_from_problem(df, problem_definition)
37     return predictor_from_code(predictor_class_str)
38 
39 
40 def json_ai_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> JsonAI:
41     """
42     Creates a JsonAI from your raw data and problem definition. Usually you would use this when you want to subsequently edit the JsonAI, the easiest way to do this is to unload it to a dictionary via `to_dict`, modify it, and then create a new object from it using `lightwood.JsonAI.from_dict`. It's usually better to generate the JsonAI using this function rather than writing it from scratch.
43 
44     :param df: The raw data
45     :param problem_definition: The manual specifications for your predictive problem
46 
47     :returns: A ``JsonAI`` object generated based on your data and problem specifications
48     """ # noqa
49     if not isinstance(problem_definition, ProblemDefinition):
50         problem_definition = ProblemDefinition.from_dict(problem_definition)
51 
52     log.info(f'Dropping features: {problem_definition.ignore_features}')
53     df = df.drop(columns=problem_definition.ignore_features)
54 
55     type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)
56     statistical_analysis = lightwood.data.statistical_analysis(
57         df, type_information.dtypes, type_information.identifiers, problem_definition)
58     json_ai = generate_json_ai(
59         type_information=type_information, statistical_analysis=statistical_analysis,
60         problem_definition=problem_definition)
61 
62     return json_ai
63 
64 
65 def code_from_json_ai(json_ai: JsonAI) -> str:
66     """
67     Autogenerates custom code based on the details you specified inside your JsonAI.
68 
69     :param json_ai: A ``JsonAI`` object
70 
71     :returns: Code (text) generate based on the ``JsonAI`` you created
72     """
73     return _code_from_json_ai(json_ai)
74 
75 
76 def predictor_from_code(code: str) -> PredictorInterface:
77     """
78     :param code: The ``Predictor``'s code in text form
79 
80     :returns: A lightwood ``Predictor`` object
81     """
82     module_name = ''.join(random.choices(string.ascii_uppercase + string.digits, k=12))
83     module_name += str(time.time()).replace('.', '')
84     predictor = _module_from_code(code, module_name).Predictor()
85     return predictor
86 
87 
88 def analyze_dataset(df: pd.DataFrame) -> DataAnalysis:
89     """
90     You can use this to understand and visualize the data, it's not a part of the pipeline one would use for creating and training predictive models.
91 
92     :param df: The raw data
93 
94     :returns: An object containing insights about the data (specifically the type information and statistical analysis)
95     """ # noqa
96 
97     problem_definition = ProblemDefinition.from_dict({'target': str(df.columns[0])})
98 
99     type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)
100     statistical_analysis = lightwood.data.statistical_analysis(
101         df, type_information.dtypes, type_information.identifiers, problem_definition)
102 
103     return DataAnalysis(
104         type_information=type_information,
105         statistical_analysis=statistical_analysis
106     )
107 
108 
109 def code_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> str:
110     """
111     :param df: The raw data
112     :param problem_definition: The manual specifications for your predictive problem
113 
114     :returns: The text code generated based on your data and problem specifications
115     """
116     if not isinstance(problem_definition, ProblemDefinition):
117         problem_definition = ProblemDefinition.from_dict(problem_definition)
118 
119     log.info(f'Dropping features: {problem_definition.ignore_features}')
120     df = df.drop(columns=problem_definition.ignore_features)
121     json_ai = json_ai_from_problem(df, problem_definition)
122     predictor_code = code_from_json_ai(json_ai)
123     return predictor_code
124 
125 
126 def predictor_from_state(state_file: str, code: str = None) -> PredictorInterface:
127     """
128     :param state_file: The file containing the pickle resulting from calling ``save`` on a ``Predictor`` object
129     :param code: The ``Predictor``'s code in text form
130 
131     :returns: A lightwood ``Predictor`` object
132     """
133     try:
134         module_name = None
135         with open(state_file, 'rb') as fp:
136             predictor = dill.load(fp)
137     except Exception as e:
138         module_name = str(e).lstrip("No module named '").split("'")[0]
139         if code is None:
140             raise Exception(
141                 'Provide code when loading a predictor from outside the scope/script it was created in!')
142 
143     if module_name is not None:
144         try:
145             del sys.modules[module_name]
146         except Exception:
147             pass
148         gc.collect()
149         _module_from_code(code, module_name)
150         with open(state_file, 'rb') as fp:
151             predictor = dill.load(fp)
152 
153     return predictor
154 
155 
156 def _module_from_code(code: str, module_name: str) -> ModuleType:
157     """
158     Create a python module (containing the generated ``Predictor`` class) from the code. This is both a python object and an associated temporary file on your filesystem
159 
160     :param code: The ``Predictor``'s code in text form
161     :param module_name: The name of the newly created module
162 
163     :returns: A python module object
164     """ # noqa
165     dirname = tempfile.gettempdir()
166     filename = os.urandom(24).hex() + str(time.time()).replace('.', '') + '.py'
167     path = os.path.join(dirname, filename)
168     if 'LIGHTWOOD_DEV_SAVE_TO' in os.environ:
169         path = os.environ['LIGHTWOOD_DEV_SAVE_TO']
170 
171     with open(path, 'wb') as fp:
172         fp.write(code.encode('utf-8'))
173         spec = importlib.util.spec_from_file_location(module_name, fp.name)
174         temp_module = importlib.util.module_from_spec(spec)
175         sys.modules[module_name] = temp_module
176         spec.loader.exec_module(temp_module)
177 
178     return temp_module
179
```
--- 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/lightwood/api/high_level.py b/lightwood/api/high_level.py
--- a/lightwood/api/high_level.py
+++ b/lightwood/api/high_level.py
@@ -176,3 +176,16 @@
         spec.loader.exec_module(temp_module)
 
     return temp_module
+
+
+def predictor_from_json_ai(json_ai: JsonAI) -> PredictorInterface:
+    """
+    Creates a ready-to-train ``Predictor`` object based on the details you specified inside your JsonAI.
+
+    :param json_ai: A ``JsonAI`` object
+
+    :returns: A lightwood ``Predictor`` object
+    """  # noqa
+    code = code_from_json_ai(json_ai)
+    predictor = predictor_from_code(code)
+    return predictor

{"golden_diff": "diff --git a/lightwood/api/high_level.py b/lightwood/api/high_level.py\n--- a/lightwood/api/high_level.py\n+++ b/lightwood/api/high_level.py\n@@ -176,3 +176,16 @@\n         spec.loader.exec_module(temp_module)\n \n     return temp_module\n+\n+\n+def predictor_from_json_ai(json_ai: JsonAI) -> PredictorInterface:\n+    \"\"\"\n+    Creates a ready-to-train ``Predictor`` object based on the details you specified inside your JsonAI.\n+\n+    :param json_ai: A ``JsonAI`` object\n+\n+    :returns: A lightwood ``Predictor`` object\n+    \"\"\"  # noqa\n+    code = code_from_json_ai(json_ai)\n+    predictor = predictor_from_code(code)\n+    return predictor\n", "issue": "Implement a `predictor_from_json_ai` high level method\n## Task\r\n\r\nLightwood aims at offering _declarative machine learning_ capabilities by enabling users to specify _what_ they want their models to learn, without necessarily having to look at the _how_.\r\n\r\nA key ingredient for this feature is our intermediate representation called Json AI (a.k.a. _J{ai}son_). The objective behind Json AI is to build Lightwood predictors out of a JSON-like file.\r\n\r\nWe offer a variety of methods to cover the spectrum of `ProblemDefinition` <-> `Json AI` <-> `Lightwood Predictor`. However, we are currently missing a method to go _directly_ from `Json AI` to a lightwood predictor object. The current approach is:\r\n\r\n```python\r\ncode = code_from_json_ai(json_ai)\r\npredictor = predictor_from_code(code)\r\n```\r\n\r\nIn this task, we ask for an implementation of a high level function so that the user is now able to do:\r\n\r\n```python\r\npredictor = predictor_from_json_ai(json_ai)\r\n```\r\n\r\n## Steps :male_detective: :female_detective: \r\n\r\n- Fork the Lightwood repository, checkout the `staging` branch and from it create a new one.\r\n- Implement the function inside `lightwood/api/high_level.py`. Be sure to add type hints and a comprehensive docstring.\r\n- Make the PR and address any comments that reviewers might make.\r\n\r\n## Additional rewards :1st_place_medal: \r\n\r\nEach Lightwood API PR brings 3\ufe0f\u20e3 points for entry into the draw for a :computer:  Deep Learning Laptop powered by the NVIDIA RTX 3080 Max-Q GPU or other swag :shirt: :bear: .  For more info check out https://mindsdb.com/hacktoberfest/\n", "before_files": [{"content": "import os\nfrom types import ModuleType\nfrom typing import Union\nimport dill\nimport pandas as pd\nfrom lightwood.api.types import DataAnalysis, JsonAI, ProblemDefinition\nimport lightwood\nfrom lightwood.api.predictor import PredictorInterface\nfrom lightwood.api.json_ai import generate_json_ai\nimport tempfile\nfrom lightwood.api.json_ai import code_from_json_ai as _code_from_json_ai\nimport importlib.util\nimport sys\nimport random\nimport string\nimport gc\nimport time\nfrom lightwood.helpers.log import log\n\n\ndef predictor_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> PredictorInterface:\n    \"\"\"\n    Creates a ready-to-train ``Predictor`` object from some raw data and a ``ProblemDefinition``. Do not use this if you want to edit the JsonAI first. Usually you'd want to next train this predictor by calling the ``learn`` method on the same dataframe used to create it.\n\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\" # noqa\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n\n    predictor_class_str = code_from_problem(df, problem_definition)\n    return predictor_from_code(predictor_class_str)\n\n\ndef json_ai_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> JsonAI:\n    \"\"\"\n    Creates a JsonAI from your raw data and problem definition. Usually you would use this when you want to subsequently edit the JsonAI, the easiest way to do this is to unload it to a dictionary via `to_dict`, modify it, and then create a new object from it using `lightwood.JsonAI.from_dict`. It's usually better to generate the JsonAI using this function rather than writing it from scratch.\n\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: A ``JsonAI`` object generated based on your data and problem specifications\n    \"\"\" # noqa\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n\n    type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)\n    statistical_analysis = lightwood.data.statistical_analysis(\n        df, type_information.dtypes, type_information.identifiers, problem_definition)\n    json_ai = generate_json_ai(\n        type_information=type_information, statistical_analysis=statistical_analysis,\n        problem_definition=problem_definition)\n\n    return json_ai\n\n\ndef code_from_json_ai(json_ai: JsonAI) -> str:\n    \"\"\"\n    Autogenerates custom code based on the details you specified inside your JsonAI.\n\n    :param json_ai: A ``JsonAI`` object\n\n    :returns: Code (text) generate based on the ``JsonAI`` you created\n    \"\"\"\n    return _code_from_json_ai(json_ai)\n\n\ndef predictor_from_code(code: str) -> PredictorInterface:\n    \"\"\"\n    :param code: The ``Predictor``'s code in text form\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\"\n    module_name = ''.join(random.choices(string.ascii_uppercase + string.digits, k=12))\n    module_name += str(time.time()).replace('.', '')\n    predictor = _module_from_code(code, module_name).Predictor()\n    return predictor\n\n\ndef analyze_dataset(df: pd.DataFrame) -> DataAnalysis:\n    \"\"\"\n    You can use this to understand and visualize the data, it's not a part of the pipeline one would use for creating and training predictive models.\n\n    :param df: The raw data\n\n    :returns: An object containing insights about the data (specifically the type information and statistical analysis)\n    \"\"\" # noqa\n\n    problem_definition = ProblemDefinition.from_dict({'target': str(df.columns[0])})\n\n    type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)\n    statistical_analysis = lightwood.data.statistical_analysis(\n        df, type_information.dtypes, type_information.identifiers, problem_definition)\n\n    return DataAnalysis(\n        type_information=type_information,\n        statistical_analysis=statistical_analysis\n    )\n\n\ndef code_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> str:\n    \"\"\"\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: The text code generated based on your data and problem specifications\n    \"\"\"\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n    json_ai = json_ai_from_problem(df, problem_definition)\n    predictor_code = code_from_json_ai(json_ai)\n    return predictor_code\n\n\ndef predictor_from_state(state_file: str, code: str = None) -> PredictorInterface:\n    \"\"\"\n    :param state_file: The file containing the pickle resulting from calling ``save`` on a ``Predictor`` object\n    :param code: The ``Predictor``'s code in text form\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\"\n    try:\n        module_name = None\n        with open(state_file, 'rb') as fp:\n            predictor = dill.load(fp)\n    except Exception as e:\n        module_name = str(e).lstrip(\"No module named '\").split(\"'\")[0]\n        if code is None:\n            raise Exception(\n                'Provide code when loading a predictor from outside the scope/script it was created in!')\n\n    if module_name is not None:\n        try:\n            del sys.modules[module_name]\n        except Exception:\n            pass\n        gc.collect()\n        _module_from_code(code, module_name)\n        with open(state_file, 'rb') as fp:\n            predictor = dill.load(fp)\n\n    return predictor\n\n\ndef _module_from_code(code: str, module_name: str) -> ModuleType:\n    \"\"\"\n    Create a python module (containing the generated ``Predictor`` class) from the code. This is both a python object and an associated temporary file on your filesystem\n\n    :param code: The ``Predictor``'s code in text form\n    :param module_name: The name of the newly created module\n\n    :returns: A python module object\n    \"\"\" # noqa\n    dirname = tempfile.gettempdir()\n    filename = os.urandom(24).hex() + str(time.time()).replace('.', '') + '.py'\n    path = os.path.join(dirname, filename)\n    if 'LIGHTWOOD_DEV_SAVE_TO' in os.environ:\n        path = os.environ['LIGHTWOOD_DEV_SAVE_TO']\n\n    with open(path, 'wb') as fp:\n        fp.write(code.encode('utf-8'))\n        spec = importlib.util.spec_from_file_location(module_name, fp.name)\n        temp_module = importlib.util.module_from_spec(spec)\n        sys.modules[module_name] = temp_module\n        spec.loader.exec_module(temp_module)\n\n    return temp_module\n", "path": "lightwood/api/high_level.py"}], "after_files": [{"content": "import os\nfrom types import ModuleType\nfrom typing import Union\nimport dill\nimport pandas as pd\nfrom lightwood.api.types import DataAnalysis, JsonAI, ProblemDefinition\nimport lightwood\nfrom lightwood.api.predictor import PredictorInterface\nfrom lightwood.api.json_ai import generate_json_ai\nimport tempfile\nfrom lightwood.api.json_ai import code_from_json_ai as _code_from_json_ai\nimport importlib.util\nimport sys\nimport random\nimport string\nimport gc\nimport time\nfrom lightwood.helpers.log import log\n\n\ndef predictor_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> PredictorInterface:\n    \"\"\"\n    Creates a ready-to-train ``Predictor`` object from some raw data and a ``ProblemDefinition``. Do not use this if you want to edit the JsonAI first. Usually you'd want to next train this predictor by calling the ``learn`` method on the same dataframe used to create it.\n\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\" # noqa\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n\n    predictor_class_str = code_from_problem(df, problem_definition)\n    return predictor_from_code(predictor_class_str)\n\n\ndef json_ai_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> JsonAI:\n    \"\"\"\n    Creates a JsonAI from your raw data and problem definition. Usually you would use this when you want to subsequently edit the JsonAI, the easiest way to do this is to unload it to a dictionary via `to_dict`, modify it, and then create a new object from it using `lightwood.JsonAI.from_dict`. It's usually better to generate the JsonAI using this function rather than writing it from scratch.\n\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: A ``JsonAI`` object generated based on your data and problem specifications\n    \"\"\" # noqa\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n\n    type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)\n    statistical_analysis = lightwood.data.statistical_analysis(\n        df, type_information.dtypes, type_information.identifiers, problem_definition)\n    json_ai = generate_json_ai(\n        type_information=type_information, statistical_analysis=statistical_analysis,\n        problem_definition=problem_definition)\n\n    return json_ai\n\n\ndef code_from_json_ai(json_ai: JsonAI) -> str:\n    \"\"\"\n    Autogenerates custom code based on the details you specified inside your JsonAI.\n\n    :param json_ai: A ``JsonAI`` object\n\n    :returns: Code (text) generate based on the ``JsonAI`` you created\n    \"\"\"\n    return _code_from_json_ai(json_ai)\n\n\ndef predictor_from_code(code: str) -> PredictorInterface:\n    \"\"\"\n    :param code: The ``Predictor``'s code in text form\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\"\n    module_name = ''.join(random.choices(string.ascii_uppercase + string.digits, k=12))\n    module_name += str(time.time()).replace('.', '')\n    predictor = _module_from_code(code, module_name).Predictor()\n    return predictor\n\n\ndef analyze_dataset(df: pd.DataFrame) -> DataAnalysis:\n    \"\"\"\n    You can use this to understand and visualize the data, it's not a part of the pipeline one would use for creating and training predictive models.\n\n    :param df: The raw data\n\n    :returns: An object containing insights about the data (specifically the type information and statistical analysis)\n    \"\"\" # noqa\n\n    problem_definition = ProblemDefinition.from_dict({'target': str(df.columns[0])})\n\n    type_information = lightwood.data.infer_types(df, problem_definition.pct_invalid)\n    statistical_analysis = lightwood.data.statistical_analysis(\n        df, type_information.dtypes, type_information.identifiers, problem_definition)\n\n    return DataAnalysis(\n        type_information=type_information,\n        statistical_analysis=statistical_analysis\n    )\n\n\ndef code_from_problem(df: pd.DataFrame, problem_definition: Union[ProblemDefinition, dict]) -> str:\n    \"\"\"\n    :param df: The raw data\n    :param problem_definition: The manual specifications for your predictive problem\n\n    :returns: The text code generated based on your data and problem specifications\n    \"\"\"\n    if not isinstance(problem_definition, ProblemDefinition):\n        problem_definition = ProblemDefinition.from_dict(problem_definition)\n\n    log.info(f'Dropping features: {problem_definition.ignore_features}')\n    df = df.drop(columns=problem_definition.ignore_features)\n    json_ai = json_ai_from_problem(df, problem_definition)\n    predictor_code = code_from_json_ai(json_ai)\n    return predictor_code\n\n\ndef predictor_from_state(state_file: str, code: str = None) -> PredictorInterface:\n    \"\"\"\n    :param state_file: The file containing the pickle resulting from calling ``save`` on a ``Predictor`` object\n    :param code: The ``Predictor``'s code in text form\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\"\n    try:\n        module_name = None\n        with open(state_file, 'rb') as fp:\n            predictor = dill.load(fp)\n    except Exception as e:\n        module_name = str(e).lstrip(\"No module named '\").split(\"'\")[0]\n        if code is None:\n            raise Exception(\n                'Provide code when loading a predictor from outside the scope/script it was created in!')\n\n    if module_name is not None:\n        try:\n            del sys.modules[module_name]\n        except Exception:\n            pass\n        gc.collect()\n        _module_from_code(code, module_name)\n        with open(state_file, 'rb') as fp:\n            predictor = dill.load(fp)\n\n    return predictor\n\n\ndef _module_from_code(code: str, module_name: str) -> ModuleType:\n    \"\"\"\n    Create a python module (containing the generated ``Predictor`` class) from the code. This is both a python object and an associated temporary file on your filesystem\n\n    :param code: The ``Predictor``'s code in text form\n    :param module_name: The name of the newly created module\n\n    :returns: A python module object\n    \"\"\" # noqa\n    dirname = tempfile.gettempdir()\n    filename = os.urandom(24).hex() + str(time.time()).replace('.', '') + '.py'\n    path = os.path.join(dirname, filename)\n    if 'LIGHTWOOD_DEV_SAVE_TO' in os.environ:\n        path = os.environ['LIGHTWOOD_DEV_SAVE_TO']\n\n    with open(path, 'wb') as fp:\n        fp.write(code.encode('utf-8'))\n        spec = importlib.util.spec_from_file_location(module_name, fp.name)\n        temp_module = importlib.util.module_from_spec(spec)\n        sys.modules[module_name] = temp_module\n        spec.loader.exec_module(temp_module)\n\n    return temp_module\n\n\ndef predictor_from_json_ai(json_ai: JsonAI) -> PredictorInterface:\n    \"\"\"\n    Creates a ready-to-train ``Predictor`` object based on the details you specified inside your JsonAI.\n\n    :param json_ai: A ``JsonAI`` object\n\n    :returns: A lightwood ``Predictor`` object\n    \"\"\"  # noqa\n    code = code_from_json_ai(json_ai)\n    predictor = predictor_from_code(code)\n    return predictor\n", "path": "lightwood/api/high_level.py"}]}