You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. r"""OS routines for Java, with some attempts to support DOS, NT, and
  2. Posix functionality.
  3. This exports:
  4. - all functions from posix, nt, dos, os2, mac, or ce, e.g. unlink, stat, etc.
  5. - os.path is one of the modules posixpath, ntpath, macpath, or dospath
  6. - os.name is 'posix', 'nt', 'dos', 'os2', 'mac', 'ce' or 'riscos'
  7. - os.curdir is a string representing the current directory ('.' or ':')
  8. - os.pardir is a string representing the parent directory ('..' or '::')
  9. - os.sep is the (or a most common) pathname separator ('/' or ':' or '\\')
  10. - os.altsep is the alternate pathname separator (None or '/')
  11. - os.pathsep is the component separator used in $PATH etc
  12. - os.linesep is the line separator in text files ('\r' or '\n' or '\r\n')
  13. - os.defpath is the default search path for executables
  14. Programs that import and use 'os' stand a better chance of being
  15. portable between different platforms. Of course, they must then
  16. only use functions that are defined by all platforms (e.g., unlink
  17. and opendir), and leave all pathname manipulation to os.path
  18. (e.g., split and join).
  19. """
  20. __all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep",
  21. "defpath", "name"]
  22. import java
  23. from java.io import File, BufferedReader, InputStreamReader, IOException
  24. import javapath as path
  25. from UserDict import UserDict
  26. import string
  27. import exceptions
  28. import re
  29. import sys
  30. import thread
  31. error = OSError
  32. name = 'java' # discriminate based on JDK version?
  33. curdir = '.' # default to Posix for directory behavior, override below
  34. pardir = '..'
  35. sep = java.io.File.separator
  36. altsep = None
  37. pathsep = java.io.File.pathSeparator
  38. defpath = '.'
  39. linesep = java.lang.System.getProperty('line.separator')
  40. def _exit(n=0):
  41. java.lang.System.exit(n)
  42. def getcwd():
  43. foo = File(File("foo").getAbsolutePath())
  44. return foo.getParent()
  45. def chdir(path):
  46. raise OSError(0, 'chdir not supported in Java', path)
  47. def listdir(path):
  48. l = File(path).list()
  49. if l is None:
  50. raise OSError(0, 'No such directory', path)
  51. return list(l)
  52. def mkdir(path, mode='ignored'):
  53. if not File(path).mkdir():
  54. raise OSError(0, "couldn't make directory", path)
  55. def makedirs(path, mode='ignored'):
  56. if not File(path).mkdirs():
  57. raise OSError(0, "couldn't make directories", path)
  58. def remove(path):
  59. if not File(path).delete():
  60. raise OSError(0, "couldn't delete file", path)
  61. def rename(path, newpath):
  62. if not File(path).renameTo(File(newpath)):
  63. raise OSError(0, "couldn't rename file", path)
  64. def rmdir(path):
  65. if not File(path).delete():
  66. raise OSError(0, "couldn't delete directory", path)
  67. unlink = remove
  68. def stat(path):
  69. """The Java stat implementation only returns a small subset of
  70. the standard fields"""
  71. f = File(path)
  72. size = f.length()
  73. # Sadly, if the returned length is zero, we don't really know if the file
  74. # is zero sized or does not exist.
  75. if size == 0 and not f.exists():
  76. raise OSError(0, 'No such file or directory', path)
  77. mtime = f.lastModified() / 1000.0
  78. return (0, 0, 0, 0, 0, 0, size, mtime, mtime, 0)
  79. def utime(path, times):
  80. # Only the modification time is changed (and only on java2).
  81. if times and hasattr(File, "setLastModified"):
  82. File(path).setLastModified(long(times[1] * 1000.0))
  83. class LazyDict( UserDict ):
  84. """A lazy-populating User Dictionary.
  85. Lazy initialization is not thread-safe.
  86. """
  87. def __init__( self,
  88. dict=None,
  89. populate=None,
  90. keyTransform=None ):
  91. """dict: starting dictionary of values
  92. populate: function that returns the populated dictionary
  93. keyTransform: function to normalize the keys (e.g., toupper/None)
  94. """
  95. UserDict.__init__( self, dict )
  96. self._populated = 0
  97. self.__populateFunc = populate or (lambda: {})
  98. self._keyTransform = keyTransform or (lambda key: key)
  99. def __populate( self ):
  100. if not self._populated:
  101. self.data = self.__populateFunc()
  102. self._populated = 1 # race condition
  103. ########## extend methods from UserDict by pre-populating
  104. def __repr__(self):
  105. self.__populate()
  106. return UserDict.__repr__( self )
  107. def __cmp__(self, dict):
  108. self.__populate()
  109. return UserDict.__cmp__( self, dict )
  110. def __len__(self):
  111. self.__populate()
  112. return UserDict.__len__( self )
  113. def __getitem__(self, key):
  114. self.__populate()
  115. return UserDict.__getitem__( self, self._keyTransform(key) )
  116. def __setitem__(self, key, item):
  117. self.__populate()
  118. UserDict.__setitem__( self, self._keyTransform(key), item )
  119. def __delitem__(self, key):
  120. self.__populate()
  121. UserDict.__delitem__( self, self._keyTransform(key) )
  122. def clear(self):
  123. self.__populate()
  124. UserDict.clear( self )
  125. def copy(self):
  126. self.__populate()
  127. return UserDict.copy( self )
  128. def keys(self):
  129. self.__populate()
  130. return UserDict.keys( self )
  131. def items(self):
  132. self.__populate()
  133. return UserDict.items( self )
  134. def values(self):
  135. self.__populate()
  136. return UserDict.values( self )
  137. def has_key(self, key):
  138. self.__populate()
  139. return UserDict.has_key( self, self._keyTransform(key) )
  140. def update(self, dict):
  141. self.__populate()
  142. UserDict.update( self, dict )
  143. def get(self, key, failobj=None):
  144. self.__populate()
  145. return UserDict.get( self, self._keyTransform(key), failobj )
  146. def setdefault(self, key, failobj=None):
  147. self.__populate()
  148. return UserDict.setdefault( self, self._keyTransform(key), failobj )
  149. def popitem(self):
  150. self.__populate()
  151. return UserDict.popitem( self )
  152. class _ShellEnv:
  153. """Provide environment derived by spawning a subshell and parsing its
  154. environment. Also supports system functions and provides empty
  155. environment support for platforms with unknown shell
  156. functionality.
  157. """
  158. def __init__( self, cmd=None, getEnv=None, keyTransform=None ):
  159. """cmd: list of exec() arguments to run command in subshell, or None
  160. getEnv: shell command to list environment variables, or None
  161. keyTransform: normalization function for environment keys, or None
  162. """
  163. self.cmd = cmd
  164. self.getEnv = getEnv
  165. self.environment = LazyDict(populate=self._getEnvironment,
  166. keyTransform=keyTransform)
  167. self._keyTransform = self.environment._keyTransform
  168. ########## system
  169. def system( self, cmd ):
  170. """Imitate the standard library 'system' call.
  171. Execute 'cmd' in a shell, and send output to stdout & stderr.
  172. """
  173. p = self.execute( cmd )
  174. def println( arg, write=sys.stdout.write ):
  175. write( arg + "\n" )
  176. def printlnStdErr( arg, write=sys.stderr.write ):
  177. write( arg + "\n" )
  178. # read stderr in new thread
  179. thread.start_new_thread( self._readLines,
  180. ( p.getErrorStream(), printlnStdErr ))
  181. # read stdin in main thread
  182. self._readLines( p.getInputStream(), println )
  183. return p.waitFor()
  184. def execute( self, cmd ):
  185. """Execute cmd in a shell, and return the process instance"""
  186. shellCmd = self._formatCmd( cmd )
  187. if self.environment._populated:
  188. env = self._formatEnvironment( self.environment )
  189. else:
  190. env = None
  191. try:
  192. p = java.lang.Runtime.getRuntime().exec( shellCmd, env )
  193. return p
  194. except IOException, ex:
  195. raise OSError(
  196. 0,
  197. "Failed to execute command (%s): %s" % ( shellCmd, ex )
  198. )
  199. ########## utility methods
  200. def _readLines( self, stream, func=None ):
  201. """Read lines of stream, and either append them to return
  202. array of lines, or call func on each line.
  203. """
  204. lines = []
  205. func = func or lines.append
  206. # should read both stderror and stdout in separate threads...
  207. bufStream = BufferedReader( InputStreamReader( stream ))
  208. while 1:
  209. line = bufStream.readLine()
  210. if line is None: break
  211. func( line )
  212. return lines or None
  213. def _formatCmd( self, cmd ):
  214. """Format a command for execution in a shell."""
  215. if self.cmd is None:
  216. msgFmt = "Unable to execute commands in subshell because shell" \
  217. " functionality not implemented for OS %s with shell" \
  218. " setting %s. Failed command=%s"""
  219. raise OSError( 0, msgFmt % ( _osType, _envType, cmd ))
  220. return self.cmd + [cmd]
  221. def _formatEnvironment( self, env ):
  222. """Format enviroment in lines suitable for Runtime.exec"""
  223. lines = []
  224. for keyValue in env.items():
  225. lines.append( "%s=%s" % keyValue )
  226. return lines
  227. def _getEnvironment( self ):
  228. """Get the environment variables by spawning a subshell.
  229. This allows multi-line variables as long as subsequent lines do
  230. not have '=' signs.
  231. """
  232. env = {}
  233. if self.getEnv:
  234. try:
  235. p = self.execute( self.getEnv )
  236. lines = self._readLines( p.getInputStream() )
  237. if '=' not in lines[0]:
  238. print "getEnv command (%s) did not print environment.\n" \
  239. "Output=%s" % (
  240. self.getEnv, '\n'.join( lines )
  241. )
  242. return env
  243. for line in lines:
  244. try:
  245. i = line.index( '=' )
  246. key = self._keyTransform(line[:i])
  247. value = line[i+1:]
  248. except ValueError:
  249. # found no '=', so line is part of previous value
  250. value = '%s\n%s' % ( value, line )
  251. env[ key ] = value
  252. except OSError, ex:
  253. print "Failed to get environment, environ will be empty:", ex
  254. return env
  255. def _getOsType( os=None ):
  256. """Select the OS behavior based on os argument, 'python.os' registry
  257. setting and 'os.name' Java property.
  258. os: explicitly select desired OS. os=None to autodetect, os='None' to
  259. disable
  260. """
  261. os = os or sys.registry.getProperty( "python.os" ) or \
  262. java.lang.System.getProperty( "os.name" )
  263. _osTypeMap = (
  264. ( "nt", r"(nt)|(Windows NT)|(Windows NT 4.0)|(WindowsNT)|"
  265. r"(Windows 2000)|(Windows XP)|(Windows CE)" ),
  266. ( "dos", r"(dos)|(Windows 95)|(Windows 98)|(Windows ME)" ),
  267. ( "mac", r"(mac)|(MacOS.*)|(Darwin)" ),
  268. ( "None", r"(None)" ),
  269. ( "posix", r"(.*)" ), # default - posix seems to vary mast widely
  270. )
  271. for osType, pattern in _osTypeMap:
  272. if re.match( pattern, os ):
  273. break
  274. return osType
  275. def _getShellEnv( envType, shellCmd, envCmd, envTransform ):
  276. """Create the desired environment type.
  277. envType: 'shell' or None
  278. """
  279. if envType == "shell":
  280. return _ShellEnv( shellCmd, envCmd, envTransform )
  281. else:
  282. return _ShellEnv()
  283. _osType = _getOsType()
  284. _envType = sys.registry.getProperty("python.environment", "shell")
  285. # default to None/empty for shell and environment behavior
  286. _shellCmd = None
  287. _envCmd = None
  288. _envTransform = None
  289. # override defaults based on _osType
  290. if _osType == "nt":
  291. _shellCmd = ["cmd", "/c"]
  292. _envCmd = "set"
  293. _envTransform = string.upper
  294. elif _osType == "dos":
  295. _shellCmd = ["command.com", "/c"]
  296. _envCmd = "set"
  297. _envTransform = string.upper
  298. elif _osType == "posix":
  299. _shellCmd = ["sh", "-c"]
  300. _envCmd = "env"
  301. elif _osType == "mac":
  302. curdir = ':' # override Posix directories
  303. pardir = '::'
  304. elif _osType == "None":
  305. pass
  306. # else:
  307. # # may want a warning, but only at high verbosity:
  308. # warn( "Unknown os type '%s', using default behavior." % _osType )
  309. _shellEnv = _getShellEnv( _envType, _shellCmd, _envCmd, _envTransform )
  310. # provide environ, putenv, getenv
  311. environ = _shellEnv.environment
  312. putenv = environ.__setitem__
  313. getenv = environ.__getitem__
  314. # provide system
  315. system = _shellEnv.system
  316. ########## test code
  317. def _testGetOsType():
  318. testVals = {
  319. "Windows NT": "nt",
  320. "Windows 95": "dos",
  321. "MacOS": "mac",
  322. "Solaris": "posix",
  323. "Linux": "posix",
  324. "None": "None"
  325. }
  326. msgFmt = "_getOsType( '%s' ) should return '%s', not '%s'"
  327. # test basic mappings
  328. for key, val in testVals.items():
  329. got = _getOsType( key )
  330. assert got == val, msgFmt % ( key, val, got )
  331. def _testCmds( _shellEnv, testCmds, whichEnv ):
  332. # test commands (key) and compare output to expected output (value).
  333. # this actually executes all the commands twice, testing the return
  334. # code by calling system(), and testing some of the output by calling
  335. # execute()
  336. for cmd, pattern in testCmds:
  337. print "\nExecuting '%s' with %s environment" % (cmd, whichEnv)
  338. assert not _shellEnv.system( cmd ), \
  339. "%s failed with %s environment" % (cmd, whichEnv)
  340. line = _shellEnv._readLines(
  341. _shellEnv.execute(cmd).getInputStream())[0]
  342. assert re.match( pattern, line ), \
  343. "expected match for %s, got %s" % ( pattern, line )
  344. def _testSystem( shellEnv=_shellEnv ):
  345. # test system and environment functionality
  346. key, value = "testKey", "testValue"
  347. org = environ
  348. testCmds = [
  349. # test commands and regexes to match first line of expected
  350. # output on first and second runs
  351. # Note that the validation is incomplete for several of these
  352. # - they should validate depending on platform and pre-post, but
  353. # they don't.
  354. # no quotes, should output both words
  355. ("echo hello there", "hello there"),
  356. # should print PATH (on NT)
  357. ("echo PATH=%PATH%", "(PATH=.*;.*)|(PATH=%PATH%)"),
  358. # should print 'testKey=%testKey%' on NT before initialization,
  359. # should print 'testKey=' on 95 before initialization,
  360. # and 'testKey=testValue' after
  361. ("echo %s=%%%s%%" % (key,key),
  362. "(%s=)" % (key,)),
  363. # should print PATH (on Unix)
  364. ( "echo PATH=$PATH", "PATH=.*" ),
  365. # should print 'testKey=testValue' on Unix after initialization
  366. ( "echo %s=$%s" % (key,key),
  367. "(%s=$%s)|(%s=)|(%s=%s)" % (key, key, key, key, value ) ),
  368. # should output quotes on NT but not on Unix
  369. ( 'echo "hello there"', '"?hello there"?' ),
  370. # should print 'why' to stdout.
  371. ( r'''jython -c "import sys;sys.stdout.write( 'why\n' )"''', "why" ),
  372. # should print 'why' to stderr, but it won't right now. Have
  373. # to add the print to give some output...empty string matches every
  374. # thing...
  375. ( r'''jython -c "import sys;sys.stderr.write('why\n');print " ''',
  376. "" )
  377. ]
  378. assert not environ._populated, \
  379. "before population, environ._populated should be false"
  380. _testCmds( _shellEnv, testCmds, "default" )
  381. # trigger initialization of environment
  382. environ[ key ] = value
  383. assert environ._populated, \
  384. "after population, environ._populated should be true"
  385. assert org.get( key, None ) == value, \
  386. "expected stub to have %s set" % key
  387. assert environ.get( key, None ) == value, \
  388. "expected real environment to have %s set" % key
  389. # test system using the non-default environment
  390. _testCmds( _shellEnv, testCmds, "initialized" )
  391. assert environ.has_key( "PATH" ), \
  392. "expected environment to have PATH attribute " \
  393. "(this may not apply to all platforms!)"
  394. def _testBadShell():
  395. # attempt to get an environment with a shell that is not startable
  396. se2 = _ShellEnv( ["badshell", "-c"], "set" )
  397. str(se2.environment) # trigger initialization
  398. assert not se2.environment.items(), "environment should be empty"
  399. def _testBadGetEnv():
  400. # attempt to get an environment with a command that does not print an environment
  401. se2 = _getShellEnv( "shell", _shellCmd, _envCmd, _envTransform )
  402. se2.getEnv="echo This command does not print environment"
  403. str(se2.environment) # trigger initialization
  404. assert not se2.environment.items(), "environment should be empty"
  405. def _test():
  406. _testGetOsType()
  407. _testBadShell()
  408. _testBadGetEnv()
  409. _testSystem()