PageRenderTime 36ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/rposix_environ.py

https://bitbucket.org/pypy/pypy/
Python | 224 lines | 192 code | 11 blank | 21 comment | 2 complexity | 5a8e01f93527aa6069dcf2e087eb83ba MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import os
  2. import sys
  3. from rpython.annotator import model as annmodel
  4. from rpython.rlib._os_support import _WIN32, StringTraits, UnicodeTraits
  5. from rpython.rlib.objectmodel import enforceargs
  6. # importing rposix here creates a cycle on Windows
  7. from rpython.rtyper.controllerentry import Controller
  8. from rpython.rtyper.extfunc import register_external
  9. from rpython.rtyper.lltypesystem import rffi, lltype
  10. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  11. str0 = annmodel.s_Str0
  12. # ____________________________________________________________
  13. #
  14. # Annotation support to control access to 'os.environ' in the RPython
  15. # program
  16. class OsEnvironController(Controller):
  17. knowntype = os.environ.__class__
  18. def convert(self, obj):
  19. # 'None' is good enough, there is only one os.environ
  20. return None
  21. def getitem(self, obj, key):
  22. # in the RPython program reads of 'os.environ[key]' are
  23. # redirected here
  24. result = r_getenv(key)
  25. if result is None:
  26. raise KeyError
  27. return result
  28. @enforceargs(None, None, str0, None)
  29. def setitem(self, obj, key, value):
  30. # in the RPython program, 'os.environ[key] = value' is
  31. # redirected here
  32. r_putenv(key, value)
  33. def delitem(self, obj, key):
  34. # in the RPython program, 'del os.environ[key]' is redirected
  35. # here
  36. absent = r_getenv(key) is None
  37. # Always call unsetenv(), to get eventual OSErrors
  38. r_unsetenv(key)
  39. if absent:
  40. raise KeyError
  41. def get_keys(self, obj):
  42. # 'os.environ.keys' is redirected here - note that it's the
  43. # getattr that arrives here, not the actual method call!
  44. return r_envkeys
  45. def get_items(self, obj):
  46. # 'os.environ.items' is redirected here (not the actual method
  47. # call!)
  48. return r_envitems
  49. def get_get(self, obj):
  50. # 'os.environ.get' is redirected here (not the actual method
  51. # call!)
  52. return r_getenv
  53. # ____________________________________________________________
  54. # Access to the 'environ' external variable
  55. prefix = ''
  56. if sys.platform.startswith('darwin'):
  57. CCHARPPP = rffi.CArrayPtr(rffi.CCHARPP)
  58. _os_NSGetEnviron = rffi.llexternal(
  59. '_NSGetEnviron', [], CCHARPPP,
  60. compilation_info=ExternalCompilationInfo(includes=['crt_externs.h'])
  61. )
  62. def os_get_environ():
  63. return _os_NSGetEnviron()[0]
  64. elif _WIN32:
  65. eci = ExternalCompilationInfo(includes=['stdlib.h'])
  66. CWCHARPP = lltype.Ptr(lltype.Array(rffi.CWCHARP, hints={'nolength': True}))
  67. os_get_environ, _os_set_environ = rffi.CExternVariable(
  68. rffi.CCHARPP, '_environ', eci)
  69. get__wenviron, _set__wenviron = rffi.CExternVariable(
  70. CWCHARPP, '_wenviron', eci, c_type='wchar_t **')
  71. prefix = '_'
  72. else:
  73. os_get_environ, _os_set_environ = rffi.CExternVariable(
  74. rffi.CCHARPP, 'environ', ExternalCompilationInfo())
  75. # ____________________________________________________________
  76. #
  77. # Lower-level interface: dummy placeholders and external registations
  78. def r_envkeys():
  79. just_a_placeholder
  80. def envkeys_llimpl():
  81. environ = os_get_environ()
  82. result = []
  83. i = 0
  84. while environ[i]:
  85. name_value = rffi.charp2str(environ[i])
  86. p = name_value.find('=')
  87. if p >= 0:
  88. result.append(name_value[:p])
  89. i += 1
  90. return result
  91. register_external(r_envkeys, [], [str0], # returns a list of strings
  92. export_name='ll_os.ll_os_envkeys',
  93. llimpl=envkeys_llimpl)
  94. # ____________________________________________________________
  95. def r_envitems():
  96. just_a_placeholder
  97. def r_getenv(name):
  98. just_a_placeholder # should return None if name not found
  99. def r_putenv(name, value):
  100. just_a_placeholder
  101. os_getenv = rffi.llexternal('getenv', [rffi.CCHARP], rffi.CCHARP,
  102. releasegil=False)
  103. os_putenv = rffi.llexternal(prefix + 'putenv', [rffi.CCHARP], rffi.INT,
  104. save_err=rffi.RFFI_SAVE_ERRNO)
  105. if _WIN32:
  106. _wgetenv = rffi.llexternal('_wgetenv', [rffi.CWCHARP], rffi.CWCHARP,
  107. compilation_info=eci, releasegil=False)
  108. _wputenv = rffi.llexternal('_wputenv', [rffi.CWCHARP], rffi.INT,
  109. compilation_info=eci,
  110. save_err=rffi.RFFI_SAVE_LASTERROR)
  111. class EnvKeepalive:
  112. pass
  113. envkeepalive = EnvKeepalive()
  114. envkeepalive.byname = {}
  115. envkeepalive.bywname = {}
  116. def make_env_impls(win32=False):
  117. if not win32:
  118. traits = StringTraits()
  119. get_environ, getenv, putenv = os_get_environ, os_getenv, os_putenv
  120. byname, eq = envkeepalive.byname, '='
  121. def last_error(msg):
  122. from rpython.rlib import rposix
  123. raise OSError(rposix.get_saved_errno(), msg)
  124. else:
  125. traits = UnicodeTraits()
  126. get_environ, getenv, putenv = get__wenviron, _wgetenv, _wputenv
  127. byname, eq = envkeepalive.bywname, u'='
  128. from rpython.rlib.rwin32 import lastSavedWindowsError as last_error
  129. def envitems_llimpl():
  130. environ = get_environ()
  131. result = []
  132. i = 0
  133. while environ[i]:
  134. name_value = traits.charp2str(environ[i])
  135. p = name_value.find(eq)
  136. if p >= 0:
  137. result.append((name_value[:p], name_value[p+1:]))
  138. i += 1
  139. return result
  140. def getenv_llimpl(name):
  141. with traits.scoped_str2charp(name) as l_name:
  142. l_result = getenv(l_name)
  143. return traits.charp2str(l_result) if l_result else None
  144. def putenv_llimpl(name, value):
  145. l_string = traits.str2charp(name + eq + value)
  146. error = rffi.cast(lltype.Signed, putenv(l_string))
  147. if error:
  148. traits.free_charp(l_string)
  149. last_error("putenv failed")
  150. # keep 'l_string' alive - we know that the C library needs it
  151. # until the next call to putenv() with the same 'name'.
  152. l_oldstring = byname.get(name, lltype.nullptr(traits.CCHARP.TO))
  153. byname[name] = l_string
  154. if l_oldstring:
  155. traits.free_charp(l_oldstring)
  156. return envitems_llimpl, getenv_llimpl, putenv_llimpl
  157. envitems_llimpl, getenv_llimpl, putenv_llimpl = make_env_impls()
  158. register_external(r_envitems, [], [(str0, str0)],
  159. export_name='ll_os.ll_os_envitems',
  160. llimpl=envitems_llimpl)
  161. register_external(r_getenv, [str0],
  162. annmodel.SomeString(can_be_None=True, no_nul=True),
  163. export_name='ll_os.ll_os_getenv',
  164. llimpl=getenv_llimpl)
  165. register_external(r_putenv, [str0, str0], annmodel.s_None,
  166. export_name='ll_os.ll_os_putenv',
  167. llimpl=putenv_llimpl)
  168. # ____________________________________________________________
  169. def r_unsetenv(name):
  170. # default implementation for platforms without a real unsetenv()
  171. r_putenv(name, '')
  172. if hasattr(__import__(os.name), 'unsetenv'):
  173. os_unsetenv = rffi.llexternal('unsetenv', [rffi.CCHARP], rffi.INT,
  174. save_err=rffi.RFFI_SAVE_ERRNO)
  175. def unsetenv_llimpl(name):
  176. with rffi.scoped_str2charp(name) as l_name:
  177. error = rffi.cast(lltype.Signed, os_unsetenv(l_name))
  178. if error:
  179. from rpython.rlib import rposix
  180. raise OSError(rposix.get_saved_errno(), "os_unsetenv failed")
  181. try:
  182. l_oldstring = envkeepalive.byname[name]
  183. except KeyError:
  184. pass
  185. else:
  186. del envkeepalive.byname[name]
  187. rffi.free_charp(l_oldstring)
  188. register_external(r_unsetenv, [str0], annmodel.s_None,
  189. export_name='ll_os.ll_os_unsetenv',
  190. llimpl=unsetenv_llimpl)