Skip to content

Commit 9e16f75

Browse files
Set locale in write/print functions (#830)
* Add setlocale to write/print functions * Add test * Update CHANGELOG * Add sanity check: * Try portugese locale * Try loading de_DE locale in CI * Update locale in runner * Add comment --------- Co-authored-by: Mohammed Ghannam <mohammad.m.ghannam@gmail.com> Co-authored-by: Mohammed Ghannam <ghannam@zib.de>
1 parent d1a9895 commit 9e16f75

File tree

4 files changed

+104
-18
lines changed

4 files changed

+104
-18
lines changed

.github/workflows/coverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jobs:
4343
4444
- name: Run pyscipopt tests
4545
run: |
46+
sudo apt-get install tzdata locales -y && sudo locale-gen pt_PT && sudo update-locale # add pt_PT locale that is used in tests
4647
coverage run -m pytest
4748
coverage report -m
4849
coverage xml

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- Add SCIP function SCIPgetTreesizeEstimation and wrapper getTreesizeEstimation
77
- Add recipes sub-package
88
### Fixed
9+
- Fixed incorrect writing/printing when user had a non-default locale
910
### Changed
1011
### Removed
1112

src/pyscipopt/scip.pxi

Lines changed: 86 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ from os.path import splitext
66
import os
77
import sys
88
import warnings
9+
import locale
910

1011
cimport cython
1112
from cpython cimport Py_INCREF, Py_DECREF
@@ -1128,8 +1129,13 @@ cdef class Model:
11281129

11291130
def printVersion(self):
11301131
"""Print version, copyright information and compile mode"""
1132+
user_locale = locale.getlocale()
1133+
locale.setlocale(locale.LC_ALL, "C")
1134+
11311135
SCIPprintVersion(self._scip, NULL)
11321136

1137+
locale.setlocale(locale.LC_ALL, user_locale)
1138+
11331139
def getProbName(self):
11341140
"""Retrieve problem name"""
11351141
return bytes(SCIPgetProbName(self._scip)).decode('UTF-8')
@@ -1455,6 +1461,9 @@ cdef class Model:
14551461
:param genericnames: indicates whether the problem should be written with generic variable and constraint names (Default value = False)
14561462
14571463
"""
1464+
user_locale = locale.getlocale()
1465+
locale.setlocale(locale.LC_ALL, "C")
1466+
14581467
str_absfile = abspath(filename)
14591468
absfile = str_conversion(str_absfile)
14601469
fn, ext = splitext(absfile)
@@ -1468,6 +1477,8 @@ cdef class Model:
14681477
PY_SCIP_CALL(SCIPwriteOrigProblem(self._scip, fn, ext, genericnames))
14691478
print('wrote problem to file ' + str_absfile)
14701479

1480+
locale.setlocale(locale.LC_ALL, user_locale)
1481+
14711482
# Variable Functions
14721483

14731484
def addVar(self, name='', vtype='C', lb=0.0, ub=None, obj=0.0, pricedVar=False, pricedVarScore=1.0):
@@ -4373,9 +4384,14 @@ cdef class Model:
43734384
"""writes current LP to a file
43744385
:param filename: file name (Default value = "LP.lp")
43754386
"""
4387+
user_locale = locale.getlocale()
4388+
locale.setlocale(locale.LC_ALL, "C")
4389+
43764390
absfile = str_conversion(abspath(filename))
43774391
PY_SCIP_CALL( SCIPwriteLP(self._scip, absfile) )
43784392

4393+
locale.setlocale(locale.LC_ALL, user_locale)
4394+
43794395
def createSol(self, Heur heur = None):
43804396
"""Create a new primal solution.
43814397
@@ -4413,19 +4429,30 @@ cdef class Model:
44134429

44144430
def printBestSol(self, write_zeros=False):
44154431
"""Prints the best feasible primal solution."""
4432+
user_locale = locale.getlocale()
4433+
locale.setlocale(locale.LC_ALL, "C")
4434+
44164435
PY_SCIP_CALL(SCIPprintBestSol(self._scip, NULL, write_zeros))
44174436

4437+
locale.setlocale(locale.LC_ALL, user_locale)
4438+
44184439
def printSol(self, Solution solution=None, write_zeros=False):
4419-
"""Print the given primal solution.
4440+
"""Print the given primal solution.
4441+
4442+
Keyword arguments:
4443+
solution -- solution to print
4444+
write_zeros -- include variables that are set to zero
4445+
"""
4446+
4447+
user_locale = locale.getlocale()
4448+
locale.setlocale(locale.LC_ALL, "C")
44204449

4421-
Keyword arguments:
4422-
solution -- solution to print
4423-
write_zeros -- include variables that are set to zero
4424-
"""
4425-
if solution is None:
4426-
PY_SCIP_CALL(SCIPprintSol(self._scip, NULL, NULL, write_zeros))
4427-
else:
4428-
PY_SCIP_CALL(SCIPprintSol(self._scip, solution.sol, NULL, write_zeros))
4450+
if solution is None:
4451+
PY_SCIP_CALL(SCIPprintSol(self._scip, NULL, NULL, write_zeros))
4452+
else:
4453+
PY_SCIP_CALL(SCIPprintSol(self._scip, solution.sol, NULL, write_zeros))
4454+
4455+
locale.setlocale(locale.LC_ALL, user_locale)
44294456

44304457
def writeBestSol(self, filename="origprob.sol", write_zeros=False):
44314458
"""Write the best feasible primal solution to a file.
@@ -4434,24 +4461,35 @@ cdef class Model:
44344461
filename -- name of the output file
44354462
write_zeros -- include variables that are set to zero
44364463
"""
4464+
4465+
user_locale = locale.getlocale()
4466+
locale.setlocale(locale.LC_ALL, "C")
4467+
44374468
# use this doubled opening pattern to ensure that IOErrors are
44384469
# triggered early and in Python not in C,Cython or SCIP.
44394470
with open(filename, "w") as f:
44404471
cfile = fdopen(f.fileno(), "w")
44414472
PY_SCIP_CALL(SCIPprintBestSol(self._scip, cfile, write_zeros))
44424473

4474+
locale.setlocale(locale.LC_ALL, user_locale)
4475+
44434476
def writeBestTransSol(self, filename="transprob.sol", write_zeros=False):
44444477
"""Write the best feasible primal solution for the transformed problem to a file.
44454478
44464479
Keyword arguments:
44474480
filename -- name of the output file
44484481
write_zeros -- include variables that are set to zero
44494482
"""
4483+
user_locale = locale.getlocale()
4484+
locale.setlocale(locale.LC_ALL, "C")
4485+
44504486
# use this double opening pattern to ensure that IOErrors are
44514487
# triggered early and in python not in C, Cython or SCIP.
44524488
with open(filename, "w") as f:
44534489
cfile = fdopen(f.fileno(), "w")
44544490
PY_SCIP_CALL(SCIPprintBestTransSol(self._scip, cfile, write_zeros))
4491+
4492+
locale.setlocale(locale.LC_ALL, user_locale)
44554493

44564494
def writeSol(self, Solution solution, filename="origprob.sol", write_zeros=False):
44574495
"""Write the given primal solution to a file.
@@ -4461,11 +4499,16 @@ cdef class Model:
44614499
filename -- name of the output file
44624500
write_zeros -- include variables that are set to zero
44634501
"""
4502+
user_locale = locale.getlocale()
4503+
locale.setlocale(locale.LC_ALL, "C")
4504+
44644505
# use this doubled opening pattern to ensure that IOErrors are
44654506
# triggered early and in Python not in C,Cython or SCIP.
44664507
with open(filename, "w") as f:
44674508
cfile = fdopen(f.fileno(), "w")
44684509
PY_SCIP_CALL(SCIPprintSol(self._scip, solution.sol, cfile, write_zeros))
4510+
4511+
locale.setlocale(locale.LC_ALL, user_locale)
44694512

44704513
def writeTransSol(self, Solution solution, filename="transprob.sol", write_zeros=False):
44714514
"""Write the given transformed primal solution to a file.
@@ -4475,11 +4518,16 @@ cdef class Model:
44754518
filename -- name of the output file
44764519
write_zeros -- include variables that are set to zero
44774520
"""
4521+
user_locale = locale.getlocale()
4522+
locale.setlocale(locale.LC_ALL, "C")
4523+
44784524
# use this doubled opening pattern to ensure that IOErrors are
44794525
# triggered early and in Python not in C,Cython or SCIP.
44804526
with open(filename, "w") as f:
44814527
cfile = fdopen(f.fileno(), "w")
44824528
PY_SCIP_CALL(SCIPprintTransSol(self._scip, solution.sol, cfile, write_zeros))
4529+
4530+
locale.setlocale(locale.LC_ALL, user_locale)
44834531

44844532
# perhaps this should not be included as it implements duplicated functionality
44854533
# (as does it's namesake in SCIP)
@@ -4737,8 +4785,13 @@ cdef class Model:
47374785
:param Variable var: variable
47384786
47394787
"""
4788+
user_locale = locale.getlocale()
4789+
locale.setlocale(locale.LC_ALL, "C")
4790+
47404791
PY_SCIP_CALL(SCIPwriteVarName(self._scip, NULL, var.scip_var, False))
47414792

4793+
locale.setlocale(locale.LC_ALL, user_locale)
4794+
47424795
def getStage(self):
47434796
"""Retrieve current SCIP stage"""
47444797
return SCIPgetStage(self._scip)
@@ -4864,19 +4917,29 @@ cdef class Model:
48644917

48654918
def printStatistics(self):
48664919
"""Print statistics."""
4920+
user_locale = locale.getlocale()
4921+
locale.setlocale(locale.LC_ALL, "C")
4922+
48674923
PY_SCIP_CALL(SCIPprintStatistics(self._scip, NULL))
48684924

4925+
locale.setlocale(locale.LC_ALL, user_locale)
4926+
48694927
def writeStatistics(self, filename="origprob.stats"):
4870-
"""Write statistics to a file.
4928+
"""Write statistics to a file.
48714929
4872-
Keyword arguments:
4873-
filename -- name of the output file
4874-
"""
4875-
# use this doubled opening pattern to ensure that IOErrors are
4876-
# triggered early and in Python not in C,Cython or SCIP.
4877-
with open(filename, "w") as f:
4878-
cfile = fdopen(f.fileno(), "w")
4879-
PY_SCIP_CALL(SCIPprintStatistics(self._scip, cfile))
4930+
Keyword arguments:
4931+
filename -- name of the output file
4932+
"""
4933+
user_locale = locale.getlocale()
4934+
locale.setlocale(locale.LC_ALL, "C")
4935+
4936+
# use this doubled opening pattern to ensure that IOErrors are
4937+
# triggered early and in Python not in C,Cython or SCIP.
4938+
with open(filename, "w") as f:
4939+
cfile = fdopen(f.fileno(), "w")
4940+
PY_SCIP_CALL(SCIPprintStatistics(self._scip, cfile))
4941+
4942+
locale.setlocale(locale.LC_ALL, user_locale)
48804943

48814944
def getNLPs(self):
48824945
"""gets total number of LPs solved so far"""
@@ -5070,11 +5133,16 @@ cdef class Model:
50705133
:param onlychanged: write only modified parameters (Default value = True)
50715134
50725135
"""
5136+
user_locale = locale.getlocale()
5137+
locale.setlocale(locale.LC_ALL, "C")
5138+
50735139
str_absfile = abspath(filename)
50745140
absfile = str_conversion(str_absfile)
50755141
PY_SCIP_CALL(SCIPwriteParams(self._scip, absfile, comments, onlychanged))
50765142
print('wrote parameter settings to file ' + str_absfile)
50775143

5144+
locale.setlocale(locale.LC_ALL, user_locale)
5145+
50785146
def resetParam(self, name):
50795147
"""Reset parameter setting to its default value
50805148

tests/test_model.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,3 +300,19 @@ def test_getTreesizeEstimation():
300300
m.optimize()
301301

302302
assert m.getTreesizeEstimation() > 0
303+
304+
def test_locale():
305+
import locale
306+
307+
m = Model()
308+
x = m.addVar(lb=1.1)
309+
310+
locale.setlocale(locale.LC_NUMERIC, "pt_PT")
311+
312+
assert locale.str(1.1) == "1,1"
313+
314+
m.writeProblem("model.cip")
315+
316+
with open("model.cip") as file:
317+
assert "1,1" not in file.read()
318+

0 commit comments

Comments
 (0)