Repository: opencobra/cobrapy

Issue:
[BUG] When a new metabolite is created during build_reaction_from_string it's compartment is not set
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Problem description

I wished to use the compartment identifier on a reaction, along with using metabolites that weren't in the model already to concurrently add the metabolites while also setting their compartments

### Code sample

Code run:

```python
import cobra
model = cobra.Model()
reaction = cobra.Reaction("R1")
model.add_reactions([reaction])
reaction.build_reaction_from_string("[c]: a --> b")
```

Failing tests:
```python
assert model.metabolites[0].compartment == "c"  # Actually None
assert model.metabolites[1].compartment == "c"  # Actually None
assert model.reaction[0].compartments == set(["c"])  # Actually empty set
```


### Environment

idk

### Anything else?

The problem is this line:
https://github.com/opencobra/cobrapy/blob/0e99b1d4777972d97f030292ee9ec6bae8e22877/src/cobra/core/reaction.py#L1618

I am pretty sure it just needs to be changed to:
```python
met = Metabolite(met_id, compartment=compartment or None)
```

Discussion:
@cdiener: Agreed, do you want to send in a PR?@oxinabox: I might not have time for a bit

Solution patch:
diff --git a/release-notes/next-release.md b/release-notes/next-release.md
index 3b41cdc8..65d76e29 100644
--- a/release-notes/next-release.md
+++ b/release-notes/next-release.md
@@ -6,6 +6,9 @@
 
 Fixes failures of GPR.copy() in Python 3.13.
 
+Fix compartment not being stored for metabolites created during
+reaction.build_reaction_from_string
+
 ## Other
 
 ## Deprecated features
diff --git a/src/cobra/core/reaction.py b/src/cobra/core/reaction.py
index 50b6b63c..9b1755df 100644
--- a/src/cobra/core/reaction.py
+++ b/src/cobra/core/reaction.py
@@ -49,7 +49,7 @@ config = Configuration()
 
 # This regular expression finds any single letter compartment enclosed in
 # square brackets at the beginning of the string. For example [c] : foo --> bar
-compartment_finder = re.compile(r"^\s*(\[[A-Za-z]\])\s*:*")
+compartment_finder = re.compile(r"^\s*\[([A-Za-z])\]\s*:*")
 # Regular expressions to match the arrows
 _reversible_arrow_finder = re.compile("<(-+|=+)>")
 _forward_arrow_finder = re.compile("(-+|=+)>")
@@ -1573,7 +1573,7 @@ class Reaction(Object):
             compartment = found_compartments[0]
             reaction_str = compartment_finder.sub("", reaction_str)
         else:
-            compartment = ""
+            compartment = None
 
         # reversible case
         arrow_match = reversible_arrow_finder.search(reaction_str)
@@ -1609,13 +1609,14 @@ class Reaction(Object):
                 else:
                     met_id = term
                     num = factor
-                met_id += compartment
+                if compartment is not None:
+                    met_id += f"[{compartment}]"
                 try:
                     met = model.metabolites.get_by_id(met_id)
                 except KeyError:
                     if verbose:
                         print(f"unknown metabolite '{met_id}' created")
-                    met = Metabolite(met_id)
+                    met = Metabolite(met_id, compartment=compartment)
                 self.add_metabolites({met: num})
 
     def summary(


Test patch:
diff --git a/tests/test_core/test_core_reaction.py b/tests/test_core/test_core_reaction.py
index a0e10a01..d4813599 100644
--- a/tests/test_core/test_core_reaction.py
+++ b/tests/test_core/test_core_reaction.py
@@ -380,6 +380,19 @@ def test_build_from_string(model: Model) -> None:
         assert pgi.bounds == (0, 1000)
 
 
+def test_build_from_string_creating_metabolites() -> None:
+    """Test that metabolites are created in the correct compartment."""
+    # https://github.com/opencobra/cobrapy/issues/1418
+    model = Model()
+    reaction = Reaction("R1")
+    model.add_reactions([reaction])
+    reaction.build_reaction_from_string("[c]: a --> b")
+    assert len(model.metabolites) == 2
+    assert model.metabolites.get_by_id("a[c]").compartment == "c"
+    assert model.metabolites.get_by_id("b[c]").compartment == "c"
+    assert model.reactions.R1.compartments == set(["c"])
+
+
 def test_bounds_setter(model: Model) -> None:
     """Test reaction bounds setter."""
     rxn = model.reactions.get_by_id("PGI")