1818 #define MAGIC_OFFSET 8
1919#endif
2020
21+ // WIN32 platform use wmain, all string related functions should change to wchar_t version
22+ #ifdef WIN32
23+ #define _CHAR wchar_t
24+ #define _T (s ) L##s
25+ #define _fopen _wfopen
26+ #define _strncmp wcsncmp
27+ #define _strlen wcslen
28+ #define _PyConfig_SetArgv PyConfig_SetArgv
29+ #define _PyConfig_SetString PyConfig_SetString
30+ #define _PyUnicode_FromStringAndSize PyUnicode_FromWideChar
31+ #else
32+ #define _CHAR char
33+ #define _T (s ) s
34+ #define _fopen fopen
35+ #define _strncmp strncmp
36+ #define _strlen strlen
37+ #define _PyConfig_SetArgv PyConfig_SetBytesArgv
38+ #define _PyConfig_SetString PyConfig_SetBytesString
39+ #define _PyUnicode_FromStringAndSize PyUnicode_FromStringAndSize
40+ #endif
41+
2142
2243int createAndInitPyconcreteModule ();
23- int execPycContent (PyObject * pyc_content , const char * filepath );
24- int runFile (const char * filepath );
25- PyObject * getFullPath (const char * filepath );
44+ int execPycContent (PyObject * pyc_content , const _CHAR * filepath );
45+ int runFile (const _CHAR * filepath );
46+ int prependSysPath0 (const _CHAR * script_path );
47+ void initPython (int argc , _CHAR * argv []);
48+ PyObject * getFullPath (const _CHAR * filepath );
2649
2750
51+ #ifdef WIN32
52+ int wmain (int argc , wchar_t * argv [])
53+ #else
2854int main (int argc , char * argv [])
55+ #endif
2956{
30- #if PY_MAJOR_VERSION >= 3
31- int i , len ;
3257 int ret = RET_OK ;
33- wchar_t * * argv_ex = NULL ;
34- argv_ex = (wchar_t * * ) malloc (sizeof (wchar_t * ) * argc );
35- for (i = 0 ; i < argc ; ++ i )
36- {
37- len = mbstowcs (NULL , argv [i ], 0 );
38- argv_ex [i ] = (wchar_t * ) malloc (sizeof (wchar_t ) * (len + 1 ));
39- mbstowcs (argv_ex [i ], argv [i ], len );
40- argv_ex [i ][len ] = 0 ;
41- }
42- #else
43- char * * argv_ex = argv ;
44- #endif
4558
46- Py_SetProgramName (argv_ex [0 ]); /* optional but recommended */
4759 // PyImport_AppendInittab must set up before Py_Initialize
4860 if (PyImport_AppendInittab ("_pyconcrete" , PyInit__pyconcrete ) == -1 )
4961 {
5062 fprintf (stderr , "Error, can't load embedded _pyconcrete correctly!\n" );
5163 return RET_FAIL ;
5264 }
65+
66+ initPython (argc , argv );
5367 Py_Initialize ();
5468 PyGILState_Ensure ();
5569
@@ -62,13 +76,40 @@ int main(int argc, char *argv[])
6276
6377 if (argc >= 2 )
6478 {
65- if (argc == 2 && (strncmp (argv [1 ], "-v" , 3 )== 0 || strncmp (argv [1 ], "--version" , 10 )== 0 ))
79+ if (argc == 2 && (_strncmp (argv [1 ], _T ( "-v" ) , 3 )== 0 || _strncmp (argv [1 ], _T ( "--version" ) , 10 )== 0 ))
6680 {
6781 printf ("pyconcrete %s [Python %s]\n" , TOSTRING (PYCONCRETE_VERSION ), TOSTRING (PY_VERSION )); // defined by build-backend
6882 }
6983 else
7084 {
85+ #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION <=7
86+ #if defined(WIN32 )
87+ PySys_SetArgv (argc - 1 , argv + 1 );
88+ #else
89+ int i , len ;
90+ wchar_t * * argv_ex = NULL ;
91+ argv_ex = (wchar_t * * ) malloc (sizeof (wchar_t * ) * argc );
92+ // setup
93+ for (i = 0 ; i < argc ; ++ i )
94+ {
95+ len = mbstowcs (NULL , argv [i ], 0 );
96+ argv_ex [i ] = (wchar_t * ) malloc (sizeof (wchar_t ) * (len + 1 ));
97+ mbstowcs (argv_ex [i ], argv [i ], len );
98+ argv_ex [i ][len ] = 0 ;
99+ }
100+
101+ // set argv
71102 PySys_SetArgv (argc - 1 , argv_ex + 1 );
103+
104+ // release
105+ for (i = 0 ; i < argc ; ++ i )
106+ {
107+ free (argv_ex [i ]);
108+ }
109+ #endif
110+ #else
111+ prependSysPath0 (argv [1 ]);
112+ #endif // PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION <=7
72113 ret = runFile (argv [1 ]);
73114 }
74115 }
@@ -91,17 +132,79 @@ int main(int argc, char *argv[])
91132 }
92133
93134 Py_Finalize ();
135+ return ret ;
136+ }
94137
95- #if PY_MAJOR_VERSION >= 3
96- for (i = 0 ; i < argc ; ++ i )
138+
139+ void initPython (int argc , _CHAR * argv []) {
140+ #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION <=7
141+ #if defined(WIN32 )
142+ Py_SetProgramName (argv [0 ]);
143+ #else
144+ int len = mbstowcs (NULL , argv [0 ], 0 );
145+ wchar_t * arg0 = (wchar_t * ) malloc (sizeof (wchar_t ) * (len + 1 ));
146+ mbstowcs (arg0 , argv [0 ], len );
147+ arg0 [len ] = 0 ;
148+ Py_SetProgramName (arg0 );
149+ #endif
150+ #else
151+ PyStatus status ;
152+
153+ // ----------
154+ // PyPreConfig
155+ // ----------
156+ // On Windows platform invoke pyconcrete by subprocess may changed the console encoding to cp1252
157+ // force to set utf8 mode to avoid the issue.
158+ PyPreConfig preconfig ;
159+ PyPreConfig_InitPythonConfig (& preconfig );
160+ preconfig .utf8_mode = 1 ;
161+
162+ status = Py_PreInitialize (& preconfig );
163+ if (PyStatus_Exception (status )) {
164+ goto INIT_EXCEPTION ;
165+ }
166+
167+ // ----------
168+ // PyConfig
169+ // ----------
170+ PyConfig config ;
171+ PyConfig_InitPythonConfig (& config );
172+ config .parse_argv = 0 ;
173+ config .isolated = 1 ;
174+
175+ // Set program_name as pyconcrete. (Implicitly preinitialize Python)
176+ status = _PyConfig_SetString (& config , & config .program_name , argv [0 ]);
177+ if (PyStatus_Exception (status )) {
178+ goto INIT_EXCEPTION ;
179+ }
180+
181+ // Decode command line arguments. (Implicitly preinitialize Python)
182+ status = _PyConfig_SetArgv (& config , argc - 1 , argv + 1 );
183+ if (PyStatus_Exception (status ))
97184 {
98- free ( argv_ex [ i ]) ;
185+ goto INIT_EXCEPTION ;
99186 }
100- free (argv_ex );
101- #endif
102- return ret ;
187+
188+ status = Py_InitializeFromConfig (& config );
189+ if (PyStatus_Exception (status ))
190+ {
191+ goto INIT_EXCEPTION ;
192+ }
193+ PyConfig_Clear (& config );
194+ return ;
195+
196+ INIT_EXCEPTION :
197+ PyConfig_Clear (& config );
198+ if (PyStatus_IsExit (status ))
199+ {
200+ return status .exitcode ;
201+ }
202+ // Display the error message and exit the process with non-zero exit code
203+ Py_ExitStatusException (status );
204+ #endif // ifdef PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION <=7
103205}
104206
207+
105208int createAndInitPyconcreteModule ()
106209{
107210 int ret = 0 ;
@@ -137,7 +240,8 @@ int createAndInitPyconcreteModule()
137240 return ret ;
138241}
139242
140- int execPycContent (PyObject * pyc_content , const char * filepath )
243+
244+ int execPycContent (PyObject * pyc_content , const _CHAR * filepath )
141245{
142246 int ret = RET_OK ;
143247 PyObject * py_marshal = NULL ;
@@ -186,7 +290,8 @@ int execPycContent(PyObject* pyc_content, const char* filepath)
186290 return ret ;
187291}
188292
189- int runFile (const char * filepath )
293+
294+ int runFile (const _CHAR * filepath )
190295{
191296 FILE * src = NULL ;
192297 char * content = NULL ;
@@ -196,7 +301,7 @@ int runFile(const char* filepath)
196301 PyObject * py_plaint_content = NULL ;
197302 PyObject * py_args = NULL ;
198303
199- src = fopen (filepath , "rb" );
304+ src = _fopen (filepath , _T ( "rb" ) );
200305 if (src == NULL )
201306 {
202307 return RET_FAIL ;
@@ -230,17 +335,51 @@ int runFile(const char* filepath)
230335 return ret ;
231336}
232337
233- PyObject * getFullPath (const char * filepath )
338+
339+ /*
340+ PySys_SetArgv is deprecated since python 3.11. It's original behavior will insert script's directory into sys.path.
341+ It's replace by PyConfig, but PyConfig only update sys.path when executing Py_Main or Py_RunMain.
342+ So it's better to update sys.path by pyconcrete.
343+ */
344+ int prependSysPath0 (const _CHAR * script_path )
345+ {
346+ // script_dir = os.path.dirname(script_path)
347+ // sys.path.insert(0, script_dir)
348+ int ret = RET_OK ;
349+
350+ PyObject * py_script_path = getFullPath (script_path );
351+ PyObject * path_module = PyImport_ImportModule ("os.path" );
352+ PyObject * dirname_func = PyObject_GetAttrString (path_module , "dirname" );
353+ PyObject * args = Py_BuildValue ("(O)" , py_script_path );
354+ PyObject * script_dir = PyObject_CallObject (dirname_func , args );
355+
356+ PyObject * sys_path = PySys_GetObject ("path" );
357+ if (PyList_Insert (sys_path , 0 , script_dir ) < 0 ) {
358+ ret = RET_FAIL ;
359+ }
360+
361+ Py_XDECREF (py_script_path );
362+ Py_XDECREF (path_module );
363+ Py_XDECREF (dirname_func );
364+ Py_XDECREF (args );
365+ Py_XDECREF (script_dir );
366+ return ret ;
367+ }
368+
369+
370+ PyObject * getFullPath (const _CHAR * filepath )
234371{
235372 // import os.path
236373 // return os.path.abspath(filepath)
237374 PyObject * path_module = PyImport_ImportModule ("os.path" );
238375 PyObject * abspath_func = PyObject_GetAttrString (path_module , "abspath" );
239- PyObject * args = Py_BuildValue ("(s)" , filepath );
240- PyObject * obj = PyObject_CallObject (abspath_func , args );
376+ PyObject * py_filepath = _PyUnicode_FromStringAndSize (filepath , _strlen (filepath ));
377+ PyObject * args = Py_BuildValue ("(O)" , py_filepath );
378+ PyObject * py_file_abspath = PyObject_CallObject (abspath_func , args );
241379
242380 Py_XDECREF (path_module );
243381 Py_XDECREF (abspath_func );
382+ Py_XDECREF (py_filepath );
244383 Py_XDECREF (args );
245- return obj ;
384+ return py_file_abspath ;
246385}
0 commit comments