busser-behave 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. checksums.yaml +7 -0
  2. data/.cane +0 -0
  3. data/.gitignore +17 -0
  4. data/.tailor +4 -0
  5. data/.travis.yml +11 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE +15 -0
  9. data/README.md +41 -0
  10. data/Rakefile +68 -0
  11. data/busser-behave.gemspec +30 -0
  12. data/features/plugin_install_command.feature +11 -0
  13. data/features/plugin_list_command.feature +8 -0
  14. data/features/support/env.rb +13 -0
  15. data/features/test_command.feature +31 -0
  16. data/lib/busser/behave/version.rb +26 -0
  17. data/lib/busser/runner_plugin/behave.rb +37 -0
  18. data/vendor/behave/CHANGES.rst +483 -0
  19. data/vendor/behave/LICENSE +23 -0
  20. data/vendor/behave/MANIFEST.in +37 -0
  21. data/vendor/behave/PROJECT_INFO.rst +21 -0
  22. data/vendor/behave/README.rst +112 -0
  23. data/vendor/behave/VERSION.txt +1 -0
  24. data/vendor/behave/behave.ini +22 -0
  25. data/vendor/behave/behave/__init__.py +30 -0
  26. data/vendor/behave/behave/__main__.py +187 -0
  27. data/vendor/behave/behave/_stepimport.py +185 -0
  28. data/vendor/behave/behave/_types.py +134 -0
  29. data/vendor/behave/behave/api/__init__.py +7 -0
  30. data/vendor/behave/behave/api/async_step.py +283 -0
  31. data/vendor/behave/behave/capture.py +227 -0
  32. data/vendor/behave/behave/compat/__init__.py +5 -0
  33. data/vendor/behave/behave/compat/collections.py +20 -0
  34. data/vendor/behave/behave/configuration.py +788 -0
  35. data/vendor/behave/behave/contrib/__init__.py +0 -0
  36. data/vendor/behave/behave/contrib/scenario_autoretry.py +73 -0
  37. data/vendor/behave/behave/formatter/__init__.py +12 -0
  38. data/vendor/behave/behave/formatter/_builtins.py +39 -0
  39. data/vendor/behave/behave/formatter/_registry.py +135 -0
  40. data/vendor/behave/behave/formatter/ansi_escapes.py +91 -0
  41. data/vendor/behave/behave/formatter/base.py +200 -0
  42. data/vendor/behave/behave/formatter/formatters.py +57 -0
  43. data/vendor/behave/behave/formatter/json.py +253 -0
  44. data/vendor/behave/behave/formatter/null.py +12 -0
  45. data/vendor/behave/behave/formatter/plain.py +158 -0
  46. data/vendor/behave/behave/formatter/pretty.py +351 -0
  47. data/vendor/behave/behave/formatter/progress.py +287 -0
  48. data/vendor/behave/behave/formatter/rerun.py +114 -0
  49. data/vendor/behave/behave/formatter/sphinx_steps.py +372 -0
  50. data/vendor/behave/behave/formatter/sphinx_util.py +118 -0
  51. data/vendor/behave/behave/formatter/steps.py +497 -0
  52. data/vendor/behave/behave/formatter/tags.py +178 -0
  53. data/vendor/behave/behave/i18n.py +614 -0
  54. data/vendor/behave/behave/importer.py +102 -0
  55. data/vendor/behave/behave/json_parser.py +264 -0
  56. data/vendor/behave/behave/log_capture.py +233 -0
  57. data/vendor/behave/behave/matchers.py +402 -0
  58. data/vendor/behave/behave/model.py +1737 -0
  59. data/vendor/behave/behave/model_core.py +416 -0
  60. data/vendor/behave/behave/model_describe.py +105 -0
  61. data/vendor/behave/behave/parser.py +615 -0
  62. data/vendor/behave/behave/reporter/__init__.py +0 -0
  63. data/vendor/behave/behave/reporter/base.py +45 -0
  64. data/vendor/behave/behave/reporter/junit.py +473 -0
  65. data/vendor/behave/behave/reporter/summary.py +94 -0
  66. data/vendor/behave/behave/runner.py +753 -0
  67. data/vendor/behave/behave/runner_util.py +417 -0
  68. data/vendor/behave/behave/step_registry.py +112 -0
  69. data/vendor/behave/behave/tag_expression.py +111 -0
  70. data/vendor/behave/behave/tag_matcher.py +465 -0
  71. data/vendor/behave/behave/textutil.py +137 -0
  72. data/vendor/behave/behave/userdata.py +130 -0
  73. data/vendor/behave/behave4cmd0/__all_steps__.py +12 -0
  74. data/vendor/behave/behave4cmd0/__init__.py +5 -0
  75. data/vendor/behave/behave4cmd0/__setup.py +11 -0
  76. data/vendor/behave/behave4cmd0/command_shell.py +216 -0
  77. data/vendor/behave/behave4cmd0/command_shell_proc.py +256 -0
  78. data/vendor/behave/behave4cmd0/command_steps.py +532 -0
  79. data/vendor/behave/behave4cmd0/command_util.py +147 -0
  80. data/vendor/behave/behave4cmd0/failing_steps.py +49 -0
  81. data/vendor/behave/behave4cmd0/log/__init__.py +1 -0
  82. data/vendor/behave/behave4cmd0/log/steps.py +395 -0
  83. data/vendor/behave/behave4cmd0/note_steps.py +29 -0
  84. data/vendor/behave/behave4cmd0/passing_steps.py +36 -0
  85. data/vendor/behave/behave4cmd0/pathutil.py +146 -0
  86. data/vendor/behave/behave4cmd0/setup_command_shell.py +24 -0
  87. data/vendor/behave/behave4cmd0/textutil.py +304 -0
  88. data/vendor/behave/bin/behave +44 -0
  89. data/vendor/behave/bin/behave.cmd +10 -0
  90. data/vendor/behave/bin/behave.junit_filter.py +85 -0
  91. data/vendor/behave/bin/behave.step_durations.py +163 -0
  92. data/vendor/behave/bin/behave2cucumber_json.py +63 -0
  93. data/vendor/behave/bin/behave_cmd.py +44 -0
  94. data/vendor/behave/bin/convert_i18n_yaml.py +77 -0
  95. data/vendor/behave/bin/explore_platform_encoding.py +24 -0
  96. data/vendor/behave/bin/i18n.yml +621 -0
  97. data/vendor/behave/bin/invoke +8 -0
  98. data/vendor/behave/bin/invoke.cmd +9 -0
  99. data/vendor/behave/bin/json.format.py +167 -0
  100. data/vendor/behave/bin/jsonschema_validate.py +122 -0
  101. data/vendor/behave/bin/make_localpi.py +279 -0
  102. data/vendor/behave/bin/project_bootstrap.sh +30 -0
  103. data/vendor/behave/bin/toxcmd.py +270 -0
  104. data/vendor/behave/bin/toxcmd3.py +270 -0
  105. data/vendor/behave/conftest.py +27 -0
  106. data/vendor/behave/docs/Makefile +154 -0
  107. data/vendor/behave/docs/_static/agogo.css +501 -0
  108. data/vendor/behave/docs/_static/behave_logo.png +0 -0
  109. data/vendor/behave/docs/_static/behave_logo1.png +0 -0
  110. data/vendor/behave/docs/_static/behave_logo2.png +0 -0
  111. data/vendor/behave/docs/_static/behave_logo3.png +0 -0
  112. data/vendor/behave/docs/_themes/LICENSE +45 -0
  113. data/vendor/behave/docs/_themes/kr/layout.html +17 -0
  114. data/vendor/behave/docs/_themes/kr/relations.html +19 -0
  115. data/vendor/behave/docs/_themes/kr/static/flasky.css_t +480 -0
  116. data/vendor/behave/docs/_themes/kr/static/small_flask.css +90 -0
  117. data/vendor/behave/docs/_themes/kr/theme.conf +7 -0
  118. data/vendor/behave/docs/_themes/kr_small/layout.html +22 -0
  119. data/vendor/behave/docs/_themes/kr_small/static/flasky.css_t +287 -0
  120. data/vendor/behave/docs/_themes/kr_small/theme.conf +10 -0
  121. data/vendor/behave/docs/api.rst +408 -0
  122. data/vendor/behave/docs/appendix.rst +19 -0
  123. data/vendor/behave/docs/behave.rst +640 -0
  124. data/vendor/behave/docs/behave.rst-template +86 -0
  125. data/vendor/behave/docs/behave_ecosystem.rst +81 -0
  126. data/vendor/behave/docs/comparison.rst +85 -0
  127. data/vendor/behave/docs/conf.py +293 -0
  128. data/vendor/behave/docs/context_attributes.rst +66 -0
  129. data/vendor/behave/docs/django.rst +192 -0
  130. data/vendor/behave/docs/formatters.rst +61 -0
  131. data/vendor/behave/docs/gherkin.rst +673 -0
  132. data/vendor/behave/docs/index.rst +57 -0
  133. data/vendor/behave/docs/install.rst +60 -0
  134. data/vendor/behave/docs/more_info.rst +184 -0
  135. data/vendor/behave/docs/new_and_noteworthy.rst +18 -0
  136. data/vendor/behave/docs/new_and_noteworthy_v1.2.4.rst +11 -0
  137. data/vendor/behave/docs/new_and_noteworthy_v1.2.5.rst +814 -0
  138. data/vendor/behave/docs/new_and_noteworthy_v1.2.6.rst +255 -0
  139. data/vendor/behave/docs/parse_builtin_types.rst +59 -0
  140. data/vendor/behave/docs/philosophy.rst +235 -0
  141. data/vendor/behave/docs/regular_expressions.rst +71 -0
  142. data/vendor/behave/docs/related.rst +14 -0
  143. data/vendor/behave/docs/test_domains.rst +62 -0
  144. data/vendor/behave/docs/tutorial.rst +636 -0
  145. data/vendor/behave/docs/update_behave_rst.py +100 -0
  146. data/vendor/behave/etc/json/behave.json-schema +172 -0
  147. data/vendor/behave/etc/junit.xml/behave_junit.xsd +103 -0
  148. data/vendor/behave/etc/junit.xml/junit-4.xsd +92 -0
  149. data/vendor/behave/examples/async_step/README.txt +8 -0
  150. data/vendor/behave/examples/async_step/behave.ini +14 -0
  151. data/vendor/behave/examples/async_step/features/async_dispatch.feature +8 -0
  152. data/vendor/behave/examples/async_step/features/async_run.feature +6 -0
  153. data/vendor/behave/examples/async_step/features/environment.py +28 -0
  154. data/vendor/behave/examples/async_step/features/steps/async_dispatch_steps.py +26 -0
  155. data/vendor/behave/examples/async_step/features/steps/async_steps34.py +10 -0
  156. data/vendor/behave/examples/async_step/features/steps/async_steps35.py +10 -0
  157. data/vendor/behave/examples/async_step/testrun_example.async_dispatch.txt +11 -0
  158. data/vendor/behave/examples/async_step/testrun_example.async_run.txt +9 -0
  159. data/vendor/behave/examples/env_vars/README.rst +26 -0
  160. data/vendor/behave/examples/env_vars/behave.ini +15 -0
  161. data/vendor/behave/examples/env_vars/behave_run.output_example.txt +12 -0
  162. data/vendor/behave/examples/env_vars/features/env_var.feature +6 -0
  163. data/vendor/behave/examples/env_vars/features/steps/env_var_steps.py +38 -0
  164. data/vendor/behave/features/README.txt +12 -0
  165. data/vendor/behave/features/background.feature +392 -0
  166. data/vendor/behave/features/capture_stderr.feature +172 -0
  167. data/vendor/behave/features/capture_stdout.feature +125 -0
  168. data/vendor/behave/features/cmdline.lang_list.feature +33 -0
  169. data/vendor/behave/features/configuration.default_paths.feature +116 -0
  170. data/vendor/behave/features/context.global_params.feature +35 -0
  171. data/vendor/behave/features/context.local_params.feature +17 -0
  172. data/vendor/behave/features/directory_layout.advanced.feature +147 -0
  173. data/vendor/behave/features/directory_layout.basic.feature +75 -0
  174. data/vendor/behave/features/directory_layout.basic2.feature +87 -0
  175. data/vendor/behave/features/environment.py +53 -0
  176. data/vendor/behave/features/exploratory_testing.with_table.feature +141 -0
  177. data/vendor/behave/features/feature.description.feature +0 -0
  178. data/vendor/behave/features/feature.exclude_from_run.feature +96 -0
  179. data/vendor/behave/features/formatter.help.feature +30 -0
  180. data/vendor/behave/features/formatter.json.feature +420 -0
  181. data/vendor/behave/features/formatter.progress3.feature +235 -0
  182. data/vendor/behave/features/formatter.rerun.feature +296 -0
  183. data/vendor/behave/features/formatter.steps.feature +181 -0
  184. data/vendor/behave/features/formatter.steps_catalog.feature +100 -0
  185. data/vendor/behave/features/formatter.steps_doc.feature +140 -0
  186. data/vendor/behave/features/formatter.steps_usage.feature +404 -0
  187. data/vendor/behave/features/formatter.tags.feature +134 -0
  188. data/vendor/behave/features/formatter.tags_location.feature +183 -0
  189. data/vendor/behave/features/formatter.user_defined.feature +196 -0
  190. data/vendor/behave/features/i18n.unicode_problems.feature +445 -0
  191. data/vendor/behave/features/logcapture.clear_handlers.feature +114 -0
  192. data/vendor/behave/features/logcapture.feature +188 -0
  193. data/vendor/behave/features/logcapture.filter.feature +130 -0
  194. data/vendor/behave/features/logging.no_capture.feature +99 -0
  195. data/vendor/behave/features/logging.setup_format.feature +157 -0
  196. data/vendor/behave/features/logging.setup_level.feature +168 -0
  197. data/vendor/behave/features/logging.setup_with_configfile.feature +137 -0
  198. data/vendor/behave/features/parser.background.sad_cases.feature +129 -0
  199. data/vendor/behave/features/parser.feature.sad_cases.feature +144 -0
  200. data/vendor/behave/features/runner.abort_by_user.feature +305 -0
  201. data/vendor/behave/features/runner.continue_after_failed_step.feature +136 -0
  202. data/vendor/behave/features/runner.default_format.feature +175 -0
  203. data/vendor/behave/features/runner.dry_run.feature +184 -0
  204. data/vendor/behave/features/runner.feature_listfile.feature +223 -0
  205. data/vendor/behave/features/runner.hook_errors.feature +382 -0
  206. data/vendor/behave/features/runner.multiple_formatters.feature +285 -0
  207. data/vendor/behave/features/runner.scenario_autoretry.feature +131 -0
  208. data/vendor/behave/features/runner.select_files_by_regexp.example.feature +71 -0
  209. data/vendor/behave/features/runner.select_files_by_regexp.feature +84 -0
  210. data/vendor/behave/features/runner.select_scenarios_by_file_location.feature +403 -0
  211. data/vendor/behave/features/runner.select_scenarios_by_name.feature +289 -0
  212. data/vendor/behave/features/runner.select_scenarios_by_tag.feature +225 -0
  213. data/vendor/behave/features/runner.stop_after_failure.feature +122 -0
  214. data/vendor/behave/features/runner.tag_logic.feature +67 -0
  215. data/vendor/behave/features/runner.unknown_formatter.feature +23 -0
  216. data/vendor/behave/features/runner.use_stage_implementations.feature +126 -0
  217. data/vendor/behave/features/scenario.description.feature +171 -0
  218. data/vendor/behave/features/scenario.exclude_from_run.feature +217 -0
  219. data/vendor/behave/features/scenario_outline.basics.feature +100 -0
  220. data/vendor/behave/features/scenario_outline.improved.feature +177 -0
  221. data/vendor/behave/features/scenario_outline.name_annotation.feature +157 -0
  222. data/vendor/behave/features/scenario_outline.parametrized.feature +401 -0
  223. data/vendor/behave/features/scenario_outline.tagged_examples.feature +118 -0
  224. data/vendor/behave/features/step.async_steps.feature +225 -0
  225. data/vendor/behave/features/step.duplicated_step.feature +106 -0
  226. data/vendor/behave/features/step.execute_steps.feature +59 -0
  227. data/vendor/behave/features/step.execute_steps.with_table.feature +65 -0
  228. data/vendor/behave/features/step.import_other_step_module.feature +103 -0
  229. data/vendor/behave/features/step.pending_steps.feature +128 -0
  230. data/vendor/behave/features/step.undefined_steps.feature +307 -0
  231. data/vendor/behave/features/step.use_step_library.feature +44 -0
  232. data/vendor/behave/features/step_dialect.generic_steps.feature +189 -0
  233. data/vendor/behave/features/step_dialect.given_when_then.feature +89 -0
  234. data/vendor/behave/features/step_param.builtin_types.with_float.feature +239 -0
  235. data/vendor/behave/features/step_param.builtin_types.with_integer.feature +305 -0
  236. data/vendor/behave/features/step_param.custom_types.feature +134 -0
  237. data/vendor/behave/features/steps/behave_active_tags_steps.py +86 -0
  238. data/vendor/behave/features/steps/behave_context_steps.py +67 -0
  239. data/vendor/behave/features/steps/behave_model_tag_logic_steps.py +105 -0
  240. data/vendor/behave/features/steps/behave_model_util.py +105 -0
  241. data/vendor/behave/features/steps/behave_select_files_steps.py +83 -0
  242. data/vendor/behave/features/steps/behave_tag_expression_steps.py +166 -0
  243. data/vendor/behave/features/steps/behave_undefined_steps.py +101 -0
  244. data/vendor/behave/features/steps/use_steplib_behave4cmd.py +12 -0
  245. data/vendor/behave/features/summary.undefined_steps.feature +114 -0
  246. data/vendor/behave/features/tags.active_tags.feature +385 -0
  247. data/vendor/behave/features/tags.default_tags.feature +104 -0
  248. data/vendor/behave/features/tags.tag_expression.feature +105 -0
  249. data/vendor/behave/features/userdata.feature +331 -0
  250. data/vendor/behave/invoke.yaml +21 -0
  251. data/vendor/behave/issue.features/README.txt +17 -0
  252. data/vendor/behave/issue.features/environment.py +97 -0
  253. data/vendor/behave/issue.features/issue0030.feature +21 -0
  254. data/vendor/behave/issue.features/issue0031.feature +16 -0
  255. data/vendor/behave/issue.features/issue0032.feature +28 -0
  256. data/vendor/behave/issue.features/issue0035.feature +74 -0
  257. data/vendor/behave/issue.features/issue0040.feature +154 -0
  258. data/vendor/behave/issue.features/issue0041.feature +135 -0
  259. data/vendor/behave/issue.features/issue0042.feature +230 -0
  260. data/vendor/behave/issue.features/issue0044.feature +51 -0
  261. data/vendor/behave/issue.features/issue0046.feature +77 -0
  262. data/vendor/behave/issue.features/issue0052.feature +66 -0
  263. data/vendor/behave/issue.features/issue0059.feature +29 -0
  264. data/vendor/behave/issue.features/issue0063.feature +102 -0
  265. data/vendor/behave/issue.features/issue0064.feature +97 -0
  266. data/vendor/behave/issue.features/issue0065.feature +18 -0
  267. data/vendor/behave/issue.features/issue0066.feature +80 -0
  268. data/vendor/behave/issue.features/issue0067.feature +90 -0
  269. data/vendor/behave/issue.features/issue0069.feature +64 -0
  270. data/vendor/behave/issue.features/issue0072.feature +32 -0
  271. data/vendor/behave/issue.features/issue0073.feature +228 -0
  272. data/vendor/behave/issue.features/issue0075.feature +18 -0
  273. data/vendor/behave/issue.features/issue0077.feature +89 -0
  274. data/vendor/behave/issue.features/issue0080.feature +49 -0
  275. data/vendor/behave/issue.features/issue0081.feature +138 -0
  276. data/vendor/behave/issue.features/issue0083.feature +69 -0
  277. data/vendor/behave/issue.features/issue0084.feature +69 -0
  278. data/vendor/behave/issue.features/issue0085.feature +119 -0
  279. data/vendor/behave/issue.features/issue0092.feature +66 -0
  280. data/vendor/behave/issue.features/issue0096.feature +173 -0
  281. data/vendor/behave/issue.features/issue0099.feature +130 -0
  282. data/vendor/behave/issue.features/issue0109.feature +60 -0
  283. data/vendor/behave/issue.features/issue0111.feature +53 -0
  284. data/vendor/behave/issue.features/issue0112.feature +64 -0
  285. data/vendor/behave/issue.features/issue0114.feature +118 -0
  286. data/vendor/behave/issue.features/issue0116.feature +71 -0
  287. data/vendor/behave/issue.features/issue0125.feature +49 -0
  288. data/vendor/behave/issue.features/issue0127.feature +64 -0
  289. data/vendor/behave/issue.features/issue0139.feature +67 -0
  290. data/vendor/behave/issue.features/issue0142.feature +37 -0
  291. data/vendor/behave/issue.features/issue0143.feature +54 -0
  292. data/vendor/behave/issue.features/issue0145.feature +63 -0
  293. data/vendor/behave/issue.features/issue0148.feature +105 -0
  294. data/vendor/behave/issue.features/issue0152.feature +52 -0
  295. data/vendor/behave/issue.features/issue0159.feature +74 -0
  296. data/vendor/behave/issue.features/issue0162.feature +86 -0
  297. data/vendor/behave/issue.features/issue0171.feature +16 -0
  298. data/vendor/behave/issue.features/issue0172.feature +51 -0
  299. data/vendor/behave/issue.features/issue0175.feature +91 -0
  300. data/vendor/behave/issue.features/issue0177.feature +40 -0
  301. data/vendor/behave/issue.features/issue0181.feature +36 -0
  302. data/vendor/behave/issue.features/issue0184.feature +144 -0
  303. data/vendor/behave/issue.features/issue0186.feature +12 -0
  304. data/vendor/behave/issue.features/issue0188.feature +60 -0
  305. data/vendor/behave/issue.features/issue0191.feature +178 -0
  306. data/vendor/behave/issue.features/issue0194.feature +215 -0
  307. data/vendor/behave/issue.features/issue0197.feature +11 -0
  308. data/vendor/behave/issue.features/issue0216.feature +129 -0
  309. data/vendor/behave/issue.features/issue0226.feature +51 -0
  310. data/vendor/behave/issue.features/issue0228.feature +41 -0
  311. data/vendor/behave/issue.features/issue0230.feature +46 -0
  312. data/vendor/behave/issue.features/issue0231.feature +77 -0
  313. data/vendor/behave/issue.features/issue0238.feature +52 -0
  314. data/vendor/behave/issue.features/issue0251.feature +15 -0
  315. data/vendor/behave/issue.features/issue0280.feature +118 -0
  316. data/vendor/behave/issue.features/issue0288.feature +95 -0
  317. data/vendor/behave/issue.features/issue0300.feature +49 -0
  318. data/vendor/behave/issue.features/issue0302.feature +91 -0
  319. data/vendor/behave/issue.features/issue0309.feature +52 -0
  320. data/vendor/behave/issue.features/issue0330.feature +124 -0
  321. data/vendor/behave/issue.features/issue0349.feature +9 -0
  322. data/vendor/behave/issue.features/issue0361.feature +79 -0
  323. data/vendor/behave/issue.features/issue0383.feature +76 -0
  324. data/vendor/behave/issue.features/issue0384.feature +103 -0
  325. data/vendor/behave/issue.features/issue0385.feature +109 -0
  326. data/vendor/behave/issue.features/issue0424.feature +66 -0
  327. data/vendor/behave/issue.features/issue0446.feature +116 -0
  328. data/vendor/behave/issue.features/issue0449.feature +42 -0
  329. data/vendor/behave/issue.features/issue0453.feature +42 -0
  330. data/vendor/behave/issue.features/issue0457.feature +65 -0
  331. data/vendor/behave/issue.features/issue0462.feature +38 -0
  332. data/vendor/behave/issue.features/issue0476.feature +39 -0
  333. data/vendor/behave/issue.features/issue0487.feature +92 -0
  334. data/vendor/behave/issue.features/issue0506.feature +77 -0
  335. data/vendor/behave/issue.features/issue0510.feature +51 -0
  336. data/vendor/behave/issue.features/requirements.txt +12 -0
  337. data/vendor/behave/issue.features/steps/ansi_steps.py +20 -0
  338. data/vendor/behave/issue.features/steps/behave_hooks_steps.py +10 -0
  339. data/vendor/behave/issue.features/steps/use_steplib_behave4cmd.py +13 -0
  340. data/vendor/behave/more.features/formatter.json.validate_output.feature +37 -0
  341. data/vendor/behave/more.features/steps/tutorial_steps.py +16 -0
  342. data/vendor/behave/more.features/steps/use_steplib_behave4cmd.py +7 -0
  343. data/vendor/behave/more.features/tutorial.feature +6 -0
  344. data/vendor/behave/py.requirements/README.txt +5 -0
  345. data/vendor/behave/py.requirements/all.txt +16 -0
  346. data/vendor/behave/py.requirements/basic.txt +21 -0
  347. data/vendor/behave/py.requirements/develop.txt +28 -0
  348. data/vendor/behave/py.requirements/docs.txt +6 -0
  349. data/vendor/behave/py.requirements/json.txt +7 -0
  350. data/vendor/behave/py.requirements/more_py26.txt +8 -0
  351. data/vendor/behave/py.requirements/testing.txt +10 -0
  352. data/vendor/behave/pytest.ini +24 -0
  353. data/vendor/behave/setup.cfg +29 -0
  354. data/vendor/behave/setup.py +118 -0
  355. data/vendor/behave/setuptools_behave.py +130 -0
  356. data/vendor/behave/tasks/__behave.py +45 -0
  357. data/vendor/behave/tasks/__init__.py +55 -0
  358. data/vendor/behave/tasks/__main__.py +70 -0
  359. data/vendor/behave/tasks/_setup.py +135 -0
  360. data/vendor/behave/tasks/_vendor/README.rst +35 -0
  361. data/vendor/behave/tasks/_vendor/invoke.zip +0 -0
  362. data/vendor/behave/tasks/_vendor/path.py +1725 -0
  363. data/vendor/behave/tasks/_vendor/pathlib.py +1280 -0
  364. data/vendor/behave/tasks/_vendor/six.py +868 -0
  365. data/vendor/behave/tasks/clean.py +246 -0
  366. data/vendor/behave/tasks/docs.py +97 -0
  367. data/vendor/behave/tasks/requirements.txt +17 -0
  368. data/vendor/behave/tasks/test.py +192 -0
  369. data/vendor/behave/test/__init__.py +0 -0
  370. data/vendor/behave/test/_importer_candidate.py +3 -0
  371. data/vendor/behave/test/reporters/__init__.py +0 -0
  372. data/vendor/behave/test/reporters/test_summary.py +240 -0
  373. data/vendor/behave/test/test_ansi_escapes.py +73 -0
  374. data/vendor/behave/test/test_configuration.py +172 -0
  375. data/vendor/behave/test/test_formatter.py +265 -0
  376. data/vendor/behave/test/test_formatter_progress.py +39 -0
  377. data/vendor/behave/test/test_formatter_rerun.py +97 -0
  378. data/vendor/behave/test/test_formatter_tags.py +57 -0
  379. data/vendor/behave/test/test_importer.py +151 -0
  380. data/vendor/behave/test/test_log_capture.py +29 -0
  381. data/vendor/behave/test/test_matchers.py +236 -0
  382. data/vendor/behave/test/test_model.py +871 -0
  383. data/vendor/behave/test/test_parser.py +1590 -0
  384. data/vendor/behave/test/test_runner.py +1074 -0
  385. data/vendor/behave/test/test_step_registry.py +96 -0
  386. data/vendor/behave/test/test_tag_expression.py +506 -0
  387. data/vendor/behave/test/test_tag_expression2.py +462 -0
  388. data/vendor/behave/test/test_tag_matcher.py +729 -0
  389. data/vendor/behave/test/test_userdata.py +184 -0
  390. data/vendor/behave/tests/README.txt +12 -0
  391. data/vendor/behave/tests/__init__.py +0 -0
  392. data/vendor/behave/tests/api/__ONLY_PY34_or_newer.txt +0 -0
  393. data/vendor/behave/tests/api/__init__.py +0 -0
  394. data/vendor/behave/tests/api/_test_async_step34.py +130 -0
  395. data/vendor/behave/tests/api/_test_async_step35.py +75 -0
  396. data/vendor/behave/tests/api/test_async_step.py +18 -0
  397. data/vendor/behave/tests/api/testing_support.py +94 -0
  398. data/vendor/behave/tests/api/testing_support_async.py +21 -0
  399. data/vendor/behave/tests/issues/test_issue0336.py +66 -0
  400. data/vendor/behave/tests/issues/test_issue0449.py +55 -0
  401. data/vendor/behave/tests/issues/test_issue0453.py +62 -0
  402. data/vendor/behave/tests/issues/test_issue0458.py +54 -0
  403. data/vendor/behave/tests/issues/test_issue0495.py +65 -0
  404. data/vendor/behave/tests/unit/__init__.py +0 -0
  405. data/vendor/behave/tests/unit/test_behave4cmd_command_shell_proc.py +135 -0
  406. data/vendor/behave/tests/unit/test_capture.py +280 -0
  407. data/vendor/behave/tests/unit/test_model_core.py +56 -0
  408. data/vendor/behave/tests/unit/test_textutil.py +267 -0
  409. data/vendor/behave/tools/test-features/background.feature +9 -0
  410. data/vendor/behave/tools/test-features/environment.py +8 -0
  411. data/vendor/behave/tools/test-features/french.feature +11 -0
  412. data/vendor/behave/tools/test-features/outline.feature +39 -0
  413. data/vendor/behave/tools/test-features/parse.feature +10 -0
  414. data/vendor/behave/tools/test-features/step-data.feature +60 -0
  415. data/vendor/behave/tools/test-features/steps/steps.py +120 -0
  416. data/vendor/behave/tools/test-features/tags.feature +18 -0
  417. data/vendor/behave/tox.ini +159 -0
  418. metadata +562 -0
@@ -0,0 +1,45 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """
3
+ Invoke build script (python based).
4
+
5
+ .. seealso:: https://github.com/pyinvoke/invoke
6
+ """
7
+
8
+ from __future__ import print_function
9
+ from invoke import task, Collection
10
+ import sys
11
+
12
+ # USE_PTY = os.isatty(sys.stdout)
13
+ USE_PTY = sys.stdout.isatty()
14
+
15
+ # ---------------------------------------------------------------------------
16
+ # TASKS
17
+ # ---------------------------------------------------------------------------
18
+ @task(help={
19
+ "args": "Command line args for behave",
20
+ "format": "Formatter to use",
21
+ })
22
+ def behave_test(ctx, args="", format=""): # XXX , echo=False):
23
+ """Run behave tests."""
24
+ format = format or ctx.behave_test.format
25
+ options = ctx.behave_test.options or ""
26
+ args = args or ctx.behave_test.args
27
+ behave = "{python} bin/behave".format(python=sys.executable)
28
+ ctx.run("{behave} -f {format} {options} {args}".format(
29
+ behave=behave, format=format, options=options, args=args),
30
+ pty=USE_PTY)
31
+
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # TASK MANAGEMENT / CONFIGURATION
35
+ # ---------------------------------------------------------------------------
36
+ # namespace.add_task(behave_test, default=True)
37
+ namespace = Collection()
38
+ namespace.add_task(behave_test, default=True)
39
+ namespace.configure({
40
+ "behave_test": {
41
+ "args": "",
42
+ "format": "progress2",
43
+ "options": "", # -- NOTE: Overide in configfile "invoke.yaml"
44
+ },
45
+ })
@@ -0,0 +1,55 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """
3
+ Invoke build script.
4
+ Show all tasks with::
5
+
6
+ invoke -l
7
+
8
+ .. seealso::
9
+
10
+ * http://pyinvoke.org
11
+ * https://github.com/pyinvoke/invoke
12
+ """
13
+
14
+ from __future__ import absolute_import
15
+
16
+ # -----------------------------------------------------------------------------
17
+ # BOOTSTRAP PATH: Use provided vendor bundle if "invoke" is not installed
18
+ # -----------------------------------------------------------------------------
19
+ INVOKE_MINVERSION = "0.14.0"
20
+ from . import _setup
21
+ _setup.setup_path()
22
+ _setup.require_invoke_minversion(INVOKE_MINVERSION)
23
+
24
+ # -----------------------------------------------------------------------------
25
+ # IMPORTS:
26
+ # -----------------------------------------------------------------------------
27
+ from invoke import Collection
28
+ import sys
29
+
30
+ # -- TASK-LIBRARY:
31
+ from . import clean
32
+ from . import docs
33
+ from . import test
34
+
35
+ # -----------------------------------------------------------------------------
36
+ # TASKS:
37
+ # -----------------------------------------------------------------------------
38
+ # None
39
+
40
+
41
+ # -----------------------------------------------------------------------------
42
+ # TASK CONFIGURATION:
43
+ # -----------------------------------------------------------------------------
44
+ namespace = Collection()
45
+ namespace.add_task(clean.clean)
46
+ namespace.add_task(clean.clean_all)
47
+ namespace.add_collection(Collection.from_module(docs))
48
+ namespace.add_collection(Collection.from_module(test))
49
+
50
+ # -- INJECT: clean configuration into this namespace
51
+ namespace.configure(clean.namespace.configuration())
52
+ if sys.platform.startswith("win"):
53
+ # -- OVERRIDE SETTINGS: For platform=win32, ... (Windows)
54
+ run_settings = dict(echo=True, pty=False, shell="C:/Windows/System32/cmd.exe")
55
+ namespace.configure({"run": run_settings})
@@ -0,0 +1,70 @@
1
+ # -*- coding: UTF-8 -*-
2
+ """
3
+ Provides "invoke" script when invoke is not installed.
4
+ Note that this approach uses the "tasks/_vendor/invoke.zip" bundle package.
5
+
6
+ Usage::
7
+
8
+ # -- INSTEAD OF: invoke command
9
+ # Show invoke version
10
+ python -m tasks --version
11
+
12
+ # List all tasks
13
+ python -m tasks -l
14
+
15
+ .. seealso::
16
+
17
+ * http://pyinvoke.org
18
+ * https://github.com/pyinvoke/invoke
19
+
20
+
21
+ Examples for Invoke Scripts using the Bundle
22
+ -------------------------------------------------------------------------------
23
+
24
+ For UNIX like platforms:
25
+
26
+ .. code-block:: sh
27
+
28
+ #!/bin/sh
29
+ #!/bin/bash
30
+ # RUN INVOKE: From bundled ZIP file (with Bourne shell/bash script).
31
+ # FILE: invoke.sh (in directory that contains tasks/ directory)
32
+
33
+ HERE=$(dirname $0)
34
+ export INVOKE_TASKS_USE_VENDOR_BUNDLES="yes"
35
+
36
+ python ${HERE}/tasks/_vendor/invoke.zip $*
37
+
38
+
39
+ For Windows platform:
40
+
41
+ .. code-block:: bat
42
+
43
+ @echo off
44
+ REM RUN INVOKE: From bundled ZIP file (with Windows Batchfile).
45
+ REM FILE: invoke.cmd (in directory that contains tasks/ directory)
46
+
47
+ setlocal
48
+ set HERE=%~dp0
49
+ set INVOKE_TASKS_USE_VENDOR_BUNDLES="yes"
50
+ if not defined PYTHON set PYTHON=python
51
+
52
+ %PYTHON% %HERE%tasks/_vendor/invoke.zip "%*"
53
+ """
54
+
55
+ from __future__ import absolute_import
56
+ import os
57
+ import sys
58
+
59
+ # -----------------------------------------------------------------------------
60
+ # BOOTSTRAP PATH: Use provided vendor bundle if "invoke" is not installed
61
+ # -----------------------------------------------------------------------------
62
+ # NOTE: tasks/__init__.py performs sys.path setup.
63
+ os.environ["INVOKE_TASKS_USE_VENDOR_BUNDLES"] = "yes"
64
+
65
+ # -----------------------------------------------------------------------------
66
+ # AUTO-MAIN:
67
+ # -----------------------------------------------------------------------------
68
+ if __name__ == "__main__":
69
+ from invoke.main import program
70
+ sys.exit(program.run())
@@ -0,0 +1,135 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Decides if vendor bundles are used or not.
4
+ Setup python path accordingly.
5
+ """
6
+
7
+ from __future__ import absolute_import, print_function
8
+ import os.path
9
+ import sys
10
+
11
+ # -----------------------------------------------------------------------------
12
+ # DEFINES:
13
+ # -----------------------------------------------------------------------------
14
+ HERE = os.path.dirname(__file__)
15
+ TASKS_VENDOR_DIR = os.path.join(HERE, "_vendor")
16
+ INVOKE_BUNDLE = os.path.join(TASKS_VENDOR_DIR, "invoke.zip")
17
+ INVOKE_BUNDLE_VERSION = "0.13.0"
18
+
19
+ DEBUG_SYSPATH = False
20
+
21
+
22
+ # -----------------------------------------------------------------------------
23
+ # EXCEPTIONS:
24
+ # -----------------------------------------------------------------------------
25
+ class VersionRequirementError(SystemExit): pass
26
+
27
+ # -----------------------------------------------------------------------------
28
+ # FUNCTIONS:
29
+ # -----------------------------------------------------------------------------
30
+ def setup_path(invoke_minversion=None):
31
+ """Setup python search and add ``TASKS_VENDOR_DIR`` (if available)."""
32
+ # print("INVOKE.tasks: setup_path")
33
+ if not os.path.isdir(TASKS_VENDOR_DIR):
34
+ print("SKIP: TASKS_VENDOR_DIR=%s is missing" % TASKS_VENDOR_DIR)
35
+ return
36
+ elif os.path.abspath(TASKS_VENDOR_DIR) in sys.path:
37
+ # -- SETUP ALREADY DONE:
38
+ # return
39
+ pass
40
+
41
+ use_vendor_bundles = os.environ.get("INVOKE_TASKS_USE_VENDOR_BUNDLES", "no")
42
+ if need_vendor_bundles(invoke_minversion):
43
+ use_vendor_bundles = "yes"
44
+
45
+ if use_vendor_bundles == "yes":
46
+ syspath_insert(0, os.path.abspath(TASKS_VENDOR_DIR))
47
+ if setup_path_for_bundle(INVOKE_BUNDLE, pos=1):
48
+ import invoke
49
+ bundle_path = os.path.relpath(INVOKE_BUNDLE, os.getcwd())
50
+ print("USING: %s (version: %s)" % (bundle_path, invoke.__version__))
51
+ else:
52
+ # -- BEST-EFFORT: May rescue something
53
+ syspath_append(os.path.abspath(TASKS_VENDOR_DIR))
54
+ setup_path_for_bundle(INVOKE_BUNDLE, pos=len(sys.path))
55
+
56
+ if DEBUG_SYSPATH:
57
+ for index, p in enumerate(sys.path):
58
+ print(" %d. %s" % (index, p))
59
+
60
+
61
+ def require_invoke_minversion(min_version, verbose=False):
62
+ """Ensures that :mod:`invoke` has at the least the :param:`min_version`.
63
+ Otherwise,
64
+
65
+ :param min_version: Minimal acceptable invoke version (as string).
66
+ :param verbose: Indicates if invoke.version should be shown.
67
+ :raises: VersionRequirementError=SystemExit if requirement fails.
68
+ """
69
+ # -- REQUIRES: sys.path is setup and contains invoke
70
+ try:
71
+ import invoke
72
+ invoke_version = invoke.__version__
73
+ except ImportError:
74
+ invoke_version = "__NOT_INSTALLED"
75
+
76
+ if invoke_version < min_version:
77
+ message = "REQUIRE: invoke.version >= %s (but was: %s)" % \
78
+ (min_version, invoke_version)
79
+ message += "\nUSE: pip install invoke>=%s" % min_version
80
+ raise VersionRequirementError(message)
81
+
82
+ INVOKE_VERSION = os.environ.get("INVOKE_VERSION", None)
83
+ if verbose and not INVOKE_VERSION:
84
+ os.environ["INVOKE_VERSION"] = invoke_version
85
+ print("USING: invoke.version=%s" % invoke_version)
86
+
87
+ def need_vendor_bundles(invoke_minversion=None):
88
+ invoke_minversion = invoke_minversion or "0.0.0"
89
+ need_vendor_answers = []
90
+ need_vendor_answers.append(need_vendor_bundle_invoke(invoke_minversion))
91
+ # -- REQUIRE: path.py
92
+ try:
93
+ import path
94
+ need_bundle = False
95
+ except ImportError:
96
+ need_bundle = True
97
+ need_vendor_answers.append(need_bundle)
98
+
99
+ # -- DIAG: print("INVOKE: need_bundle=%s" % need_bundle1)
100
+ # return need_bundle1 or need_bundle2
101
+ return any(need_vendor_answers)
102
+
103
+ def need_vendor_bundle_invoke(invoke_minversion="0.0.0"):
104
+ # -- REQUIRE: invoke
105
+ try:
106
+ import invoke
107
+ need_bundle = invoke.__version__ < invoke_minversion
108
+ if need_bundle:
109
+ del sys.modules["invoke"]
110
+ del invoke
111
+ except ImportError:
112
+ need_bundle = True
113
+ except Exception:
114
+ need_bundle = True
115
+ return need_bundle
116
+
117
+ # -----------------------------------------------------------------------------
118
+ # UTILITY FUNCTIONS:
119
+ # -----------------------------------------------------------------------------
120
+ def setup_path_for_bundle(bundle_path, pos=0):
121
+ if os.path.exists(bundle_path):
122
+ syspath_insert(pos, os.path.abspath(bundle_path))
123
+ return True
124
+ return False
125
+
126
+ def syspath_insert(pos, path):
127
+ if path in sys.path:
128
+ sys.path.remove(path)
129
+ sys.path.insert(pos, path)
130
+
131
+ def syspath_append(path):
132
+ if path in sys.path:
133
+ sys.path.remove(path)
134
+ sys.path.append(path)
135
+
@@ -0,0 +1,35 @@
1
+ tasks/_vendor: Bundled vendor parts -- needed by tasks
2
+ ===============================================================================
3
+
4
+ This directory contains bundled archives that may be needed to run the tasks.
5
+ Especially, it contains an executable "invoke.zip" archive.
6
+ This archive can be used when invoke is not installed.
7
+
8
+ To execute invoke from the bundled ZIP archive::
9
+
10
+
11
+ python -m tasks/_vendor/invoke.zip --help
12
+ python -m tasks/_vendor/invoke.zip --version
13
+
14
+
15
+ Example for a local "bin/invoke" script in a UNIX like platform environment::
16
+
17
+ #!/bin/bash
18
+ # RUN INVOKE: From bundled ZIP file.
19
+
20
+ HERE=$(dirname $0)
21
+
22
+ python ${HERE}/../tasks/_vendor/invoke.zip $*
23
+
24
+ Example for a local "bin/invoke.cmd" script in a Windows environment::
25
+
26
+ @echo off
27
+ REM ==========================================================================
28
+ REM RUN INVOKE: From bundled ZIP file.
29
+ REM ==========================================================================
30
+
31
+ setlocal
32
+ set HERE=%~dp0
33
+ if not defined PYTHON set PYTHON=python
34
+
35
+ %PYTHON% %HERE%../tasks/_vendor/invoke.zip "%*"
@@ -0,0 +1,1725 @@
1
+ #
2
+ # SOURCE: https://pypi.python.org/pypi/path.py
3
+ # VERSION: 8.2.1
4
+ # -----------------------------------------------------------------------------
5
+ # Copyright (c) 2010 Mikhail Gusarov
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # of this software and associated documentation files (the "Software"), to deal
9
+ # in the Software without restriction, including without limitation the rights
10
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # copies of the Software, and to permit persons to whom the Software is
12
+ # furnished to do so, subject to the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be included in
15
+ # all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # SOFTWARE.
24
+ #
25
+
26
+ """
27
+ path.py - An object representing a path to a file or directory.
28
+
29
+ https://github.com/jaraco/path.py
30
+
31
+ Example::
32
+
33
+ from path import Path
34
+ d = Path('/home/guido/bin')
35
+ for f in d.files('*.py'):
36
+ f.chmod(0o755)
37
+ """
38
+
39
+ from __future__ import unicode_literals
40
+
41
+ import sys
42
+ import warnings
43
+ import os
44
+ import fnmatch
45
+ import glob
46
+ import shutil
47
+ import codecs
48
+ import hashlib
49
+ import errno
50
+ import tempfile
51
+ import functools
52
+ import operator
53
+ import re
54
+ import contextlib
55
+ import io
56
+ from distutils import dir_util
57
+ import importlib
58
+
59
+ try:
60
+ import win32security
61
+ except ImportError:
62
+ pass
63
+
64
+ try:
65
+ import pwd
66
+ except ImportError:
67
+ pass
68
+
69
+ try:
70
+ import grp
71
+ except ImportError:
72
+ pass
73
+
74
+ ##############################################################################
75
+ # Python 2/3 support
76
+ PY3 = sys.version_info >= (3,)
77
+ PY2 = not PY3
78
+
79
+ string_types = str,
80
+ text_type = str
81
+ getcwdu = os.getcwd
82
+
83
+ def surrogate_escape(error):
84
+ """
85
+ Simulate the Python 3 ``surrogateescape`` handler, but for Python 2 only.
86
+ """
87
+ chars = error.object[error.start:error.end]
88
+ assert len(chars) == 1
89
+ val = ord(chars)
90
+ val += 0xdc00
91
+ return __builtin__.unichr(val), error.end
92
+
93
+ if PY2:
94
+ import __builtin__
95
+ string_types = __builtin__.basestring,
96
+ text_type = __builtin__.unicode
97
+ getcwdu = os.getcwdu
98
+ codecs.register_error('surrogateescape', surrogate_escape)
99
+
100
+ @contextlib.contextmanager
101
+ def io_error_compat():
102
+ try:
103
+ yield
104
+ except IOError as io_err:
105
+ # On Python 2, io.open raises IOError; transform to OSError for
106
+ # future compatibility.
107
+ os_err = OSError(*io_err.args)
108
+ os_err.filename = getattr(io_err, 'filename', None)
109
+ raise os_err
110
+
111
+ ##############################################################################
112
+
113
+ __all__ = ['Path', 'CaseInsensitivePattern']
114
+
115
+
116
+ LINESEPS = ['\r\n', '\r', '\n']
117
+ U_LINESEPS = LINESEPS + ['\u0085', '\u2028', '\u2029']
118
+ NEWLINE = re.compile('|'.join(LINESEPS))
119
+ U_NEWLINE = re.compile('|'.join(U_LINESEPS))
120
+ NL_END = re.compile(r'(?:{0})$'.format(NEWLINE.pattern))
121
+ U_NL_END = re.compile(r'(?:{0})$'.format(U_NEWLINE.pattern))
122
+
123
+
124
+ try:
125
+ import pkg_resources
126
+ __version__ = pkg_resources.require('path.py')[0].version
127
+ except Exception:
128
+ __version__ = '8.2.1' # XXX-MODIFIED-WAS: 'unknown'
129
+
130
+
131
+ class TreeWalkWarning(Warning):
132
+ pass
133
+
134
+
135
+ # from jaraco.functools
136
+ def compose(*funcs):
137
+ compose_two = lambda f1, f2: lambda *args, **kwargs: f1(f2(*args, **kwargs))
138
+ return functools.reduce(compose_two, funcs)
139
+
140
+
141
+ def simple_cache(func):
142
+ """
143
+ Save results for the :meth:'path.using_module' classmethod.
144
+ When Python 3.2 is available, use functools.lru_cache instead.
145
+ """
146
+ saved_results = {}
147
+
148
+ def wrapper(cls, module):
149
+ if module in saved_results:
150
+ return saved_results[module]
151
+ saved_results[module] = func(cls, module)
152
+ return saved_results[module]
153
+ return wrapper
154
+
155
+
156
+ class ClassProperty(property):
157
+ def __get__(self, cls, owner):
158
+ return self.fget.__get__(None, owner)()
159
+
160
+
161
+ class multimethod(object):
162
+ """
163
+ Acts like a classmethod when invoked from the class and like an
164
+ instancemethod when invoked from the instance.
165
+ """
166
+ def __init__(self, func):
167
+ self.func = func
168
+
169
+ def __get__(self, instance, owner):
170
+ return (
171
+ functools.partial(self.func, owner) if instance is None
172
+ else functools.partial(self.func, owner, instance)
173
+ )
174
+
175
+
176
+ class Path(text_type):
177
+ """
178
+ Represents a filesystem path.
179
+
180
+ For documentation on individual methods, consult their
181
+ counterparts in :mod:`os.path`.
182
+
183
+ Some methods are additionally included from :mod:`shutil`.
184
+ The functions are linked directly into the class namespace
185
+ such that they will be bound to the Path instance. For example,
186
+ ``Path(src).copy(target)`` is equivalent to
187
+ ``shutil.copy(src, target)``. Therefore, when referencing
188
+ the docs for these methods, assume `src` references `self`,
189
+ the Path instance.
190
+ """
191
+
192
+ module = os.path
193
+ """ The path module to use for path operations.
194
+
195
+ .. seealso:: :mod:`os.path`
196
+ """
197
+
198
+ def __init__(self, other=''):
199
+ if other is None:
200
+ raise TypeError("Invalid initial value for path: None")
201
+
202
+ @classmethod
203
+ @simple_cache
204
+ def using_module(cls, module):
205
+ subclass_name = cls.__name__ + '_' + module.__name__
206
+ if PY2:
207
+ subclass_name = str(subclass_name)
208
+ bases = (cls,)
209
+ ns = {'module': module}
210
+ return type(subclass_name, bases, ns)
211
+
212
+ @ClassProperty
213
+ @classmethod
214
+ def _next_class(cls):
215
+ """
216
+ What class should be used to construct new instances from this class
217
+ """
218
+ return cls
219
+
220
+ @classmethod
221
+ def _always_unicode(cls, path):
222
+ """
223
+ Ensure the path as retrieved from a Python API, such as :func:`os.listdir`,
224
+ is a proper Unicode string.
225
+ """
226
+ if PY3 or isinstance(path, text_type):
227
+ return path
228
+ return path.decode(sys.getfilesystemencoding(), 'surrogateescape')
229
+
230
+ # --- Special Python methods.
231
+
232
+ def __repr__(self):
233
+ return '%s(%s)' % (type(self).__name__, super(Path, self).__repr__())
234
+
235
+ # Adding a Path and a string yields a Path.
236
+ def __add__(self, more):
237
+ try:
238
+ return self._next_class(super(Path, self).__add__(more))
239
+ except TypeError: # Python bug
240
+ return NotImplemented
241
+
242
+ def __radd__(self, other):
243
+ if not isinstance(other, string_types):
244
+ return NotImplemented
245
+ return self._next_class(other.__add__(self))
246
+
247
+ # The / operator joins Paths.
248
+ def __div__(self, rel):
249
+ """ fp.__div__(rel) == fp / rel == fp.joinpath(rel)
250
+
251
+ Join two path components, adding a separator character if
252
+ needed.
253
+
254
+ .. seealso:: :func:`os.path.join`
255
+ """
256
+ return self._next_class(self.module.join(self, rel))
257
+
258
+ # Make the / operator work even when true division is enabled.
259
+ __truediv__ = __div__
260
+
261
+ # The / operator joins Paths the other way around
262
+ def __rdiv__(self, rel):
263
+ """ fp.__rdiv__(rel) == rel / fp
264
+
265
+ Join two path components, adding a separator character if
266
+ needed.
267
+
268
+ .. seealso:: :func:`os.path.join`
269
+ """
270
+ return self._next_class(self.module.join(rel, self))
271
+
272
+ # Make the / operator work even when true division is enabled.
273
+ __rtruediv__ = __rdiv__
274
+
275
+ def __enter__(self):
276
+ self._old_dir = self.getcwd()
277
+ os.chdir(self)
278
+ return self
279
+
280
+ def __exit__(self, *_):
281
+ os.chdir(self._old_dir)
282
+
283
+ @classmethod
284
+ def getcwd(cls):
285
+ """ Return the current working directory as a path object.
286
+
287
+ .. seealso:: :func:`os.getcwdu`
288
+ """
289
+ return cls(getcwdu())
290
+
291
+ #
292
+ # --- Operations on Path strings.
293
+
294
+ def abspath(self):
295
+ """ .. seealso:: :func:`os.path.abspath` """
296
+ return self._next_class(self.module.abspath(self))
297
+
298
+ def normcase(self):
299
+ """ .. seealso:: :func:`os.path.normcase` """
300
+ return self._next_class(self.module.normcase(self))
301
+
302
+ def normpath(self):
303
+ """ .. seealso:: :func:`os.path.normpath` """
304
+ return self._next_class(self.module.normpath(self))
305
+
306
+ def realpath(self):
307
+ """ .. seealso:: :func:`os.path.realpath` """
308
+ return self._next_class(self.module.realpath(self))
309
+
310
+ def expanduser(self):
311
+ """ .. seealso:: :func:`os.path.expanduser` """
312
+ return self._next_class(self.module.expanduser(self))
313
+
314
+ def expandvars(self):
315
+ """ .. seealso:: :func:`os.path.expandvars` """
316
+ return self._next_class(self.module.expandvars(self))
317
+
318
+ def dirname(self):
319
+ """ .. seealso:: :attr:`parent`, :func:`os.path.dirname` """
320
+ return self._next_class(self.module.dirname(self))
321
+
322
+ def basename(self):
323
+ """ .. seealso:: :attr:`name`, :func:`os.path.basename` """
324
+ return self._next_class(self.module.basename(self))
325
+
326
+ def expand(self):
327
+ """ Clean up a filename by calling :meth:`expandvars()`,
328
+ :meth:`expanduser()`, and :meth:`normpath()` on it.
329
+
330
+ This is commonly everything needed to clean up a filename
331
+ read from a configuration file, for example.
332
+ """
333
+ return self.expandvars().expanduser().normpath()
334
+
335
+ @property
336
+ def namebase(self):
337
+ """ The same as :meth:`name`, but with one file extension stripped off.
338
+
339
+ For example,
340
+ ``Path('/home/guido/python.tar.gz').name == 'python.tar.gz'``,
341
+ but
342
+ ``Path('/home/guido/python.tar.gz').namebase == 'python.tar'``.
343
+ """
344
+ base, ext = self.module.splitext(self.name)
345
+ return base
346
+
347
+ @property
348
+ def ext(self):
349
+ """ The file extension, for example ``'.py'``. """
350
+ f, ext = self.module.splitext(self)
351
+ return ext
352
+
353
+ @property
354
+ def drive(self):
355
+ """ The drive specifier, for example ``'C:'``.
356
+
357
+ This is always empty on systems that don't use drive specifiers.
358
+ """
359
+ drive, r = self.module.splitdrive(self)
360
+ return self._next_class(drive)
361
+
362
+ parent = property(
363
+ dirname, None, None,
364
+ """ This path's parent directory, as a new Path object.
365
+
366
+ For example,
367
+ ``Path('/usr/local/lib/libpython.so').parent ==
368
+ Path('/usr/local/lib')``
369
+
370
+ .. seealso:: :meth:`dirname`, :func:`os.path.dirname`
371
+ """)
372
+
373
+ name = property(
374
+ basename, None, None,
375
+ """ The name of this file or directory without the full path.
376
+
377
+ For example,
378
+ ``Path('/usr/local/lib/libpython.so').name == 'libpython.so'``
379
+
380
+ .. seealso:: :meth:`basename`, :func:`os.path.basename`
381
+ """)
382
+
383
+ def splitpath(self):
384
+ """ p.splitpath() -> Return ``(p.parent, p.name)``.
385
+
386
+ .. seealso:: :attr:`parent`, :attr:`name`, :func:`os.path.split`
387
+ """
388
+ parent, child = self.module.split(self)
389
+ return self._next_class(parent), child
390
+
391
+ def splitdrive(self):
392
+ """ p.splitdrive() -> Return ``(p.drive, <the rest of p>)``.
393
+
394
+ Split the drive specifier from this path. If there is
395
+ no drive specifier, :samp:`{p.drive}` is empty, so the return value
396
+ is simply ``(Path(''), p)``. This is always the case on Unix.
397
+
398
+ .. seealso:: :func:`os.path.splitdrive`
399
+ """
400
+ drive, rel = self.module.splitdrive(self)
401
+ return self._next_class(drive), rel
402
+
403
+ def splitext(self):
404
+ """ p.splitext() -> Return ``(p.stripext(), p.ext)``.
405
+
406
+ Split the filename extension from this path and return
407
+ the two parts. Either part may be empty.
408
+
409
+ The extension is everything from ``'.'`` to the end of the
410
+ last path segment. This has the property that if
411
+ ``(a, b) == p.splitext()``, then ``a + b == p``.
412
+
413
+ .. seealso:: :func:`os.path.splitext`
414
+ """
415
+ filename, ext = self.module.splitext(self)
416
+ return self._next_class(filename), ext
417
+
418
+ def stripext(self):
419
+ """ p.stripext() -> Remove one file extension from the path.
420
+
421
+ For example, ``Path('/home/guido/python.tar.gz').stripext()``
422
+ returns ``Path('/home/guido/python.tar')``.
423
+ """
424
+ return self.splitext()[0]
425
+
426
+ def splitunc(self):
427
+ """ .. seealso:: :func:`os.path.splitunc` """
428
+ unc, rest = self.module.splitunc(self)
429
+ return self._next_class(unc), rest
430
+
431
+ @property
432
+ def uncshare(self):
433
+ """
434
+ The UNC mount point for this path.
435
+ This is empty for paths on local drives.
436
+ """
437
+ unc, r = self.module.splitunc(self)
438
+ return self._next_class(unc)
439
+
440
+ @multimethod
441
+ def joinpath(cls, first, *others):
442
+ """
443
+ Join first to zero or more :class:`Path` components, adding a separator
444
+ character (:samp:`{first}.module.sep`) if needed. Returns a new instance of
445
+ :samp:`{first}._next_class`.
446
+
447
+ .. seealso:: :func:`os.path.join`
448
+ """
449
+ if not isinstance(first, cls):
450
+ first = cls(first)
451
+ return first._next_class(first.module.join(first, *others))
452
+
453
+ def splitall(self):
454
+ r""" Return a list of the path components in this path.
455
+
456
+ The first item in the list will be a Path. Its value will be
457
+ either :data:`os.curdir`, :data:`os.pardir`, empty, or the root
458
+ directory of this path (for example, ``'/'`` or ``'C:\\'``). The
459
+ other items in the list will be strings.
460
+
461
+ ``path.Path.joinpath(*result)`` will yield the original path.
462
+ """
463
+ parts = []
464
+ loc = self
465
+ while loc != os.curdir and loc != os.pardir:
466
+ prev = loc
467
+ loc, child = prev.splitpath()
468
+ if loc == prev:
469
+ break
470
+ parts.append(child)
471
+ parts.append(loc)
472
+ parts.reverse()
473
+ return parts
474
+
475
+ def relpath(self, start='.'):
476
+ """ Return this path as a relative path,
477
+ based from `start`, which defaults to the current working directory.
478
+ """
479
+ cwd = self._next_class(start)
480
+ return cwd.relpathto(self)
481
+
482
+ def relpathto(self, dest):
483
+ """ Return a relative path from `self` to `dest`.
484
+
485
+ If there is no relative path from `self` to `dest`, for example if
486
+ they reside on different drives in Windows, then this returns
487
+ ``dest.abspath()``.
488
+ """
489
+ origin = self.abspath()
490
+ dest = self._next_class(dest).abspath()
491
+
492
+ orig_list = origin.normcase().splitall()
493
+ # Don't normcase dest! We want to preserve the case.
494
+ dest_list = dest.splitall()
495
+
496
+ if orig_list[0] != self.module.normcase(dest_list[0]):
497
+ # Can't get here from there.
498
+ return dest
499
+
500
+ # Find the location where the two paths start to differ.
501
+ i = 0
502
+ for start_seg, dest_seg in zip(orig_list, dest_list):
503
+ if start_seg != self.module.normcase(dest_seg):
504
+ break
505
+ i += 1
506
+
507
+ # Now i is the point where the two paths diverge.
508
+ # Need a certain number of "os.pardir"s to work up
509
+ # from the origin to the point of divergence.
510
+ segments = [os.pardir] * (len(orig_list) - i)
511
+ # Need to add the diverging part of dest_list.
512
+ segments += dest_list[i:]
513
+ if len(segments) == 0:
514
+ # If they happen to be identical, use os.curdir.
515
+ relpath = os.curdir
516
+ else:
517
+ relpath = self.module.join(*segments)
518
+ return self._next_class(relpath)
519
+
520
+ # --- Listing, searching, walking, and matching
521
+
522
+ def listdir(self, pattern=None):
523
+ """ D.listdir() -> List of items in this directory.
524
+
525
+ Use :meth:`files` or :meth:`dirs` instead if you want a listing
526
+ of just files or just subdirectories.
527
+
528
+ The elements of the list are Path objects.
529
+
530
+ With the optional `pattern` argument, this only lists
531
+ items whose names match the given pattern.
532
+
533
+ .. seealso:: :meth:`files`, :meth:`dirs`
534
+ """
535
+ if pattern is None:
536
+ pattern = '*'
537
+ return [
538
+ self / child
539
+ for child in map(self._always_unicode, os.listdir(self))
540
+ if self._next_class(child).fnmatch(pattern)
541
+ ]
542
+
543
+ def dirs(self, pattern=None):
544
+ """ D.dirs() -> List of this directory's subdirectories.
545
+
546
+ The elements of the list are Path objects.
547
+ This does not walk recursively into subdirectories
548
+ (but see :meth:`walkdirs`).
549
+
550
+ With the optional `pattern` argument, this only lists
551
+ directories whose names match the given pattern. For
552
+ example, ``d.dirs('build-*')``.
553
+ """
554
+ return [p for p in self.listdir(pattern) if p.isdir()]
555
+
556
+ def files(self, pattern=None):
557
+ """ D.files() -> List of the files in this directory.
558
+
559
+ The elements of the list are Path objects.
560
+ This does not walk into subdirectories (see :meth:`walkfiles`).
561
+
562
+ With the optional `pattern` argument, this only lists files
563
+ whose names match the given pattern. For example,
564
+ ``d.files('*.pyc')``.
565
+ """
566
+
567
+ return [p for p in self.listdir(pattern) if p.isfile()]
568
+
569
+ def walk(self, pattern=None, errors='strict'):
570
+ """ D.walk() -> iterator over files and subdirs, recursively.
571
+
572
+ The iterator yields Path objects naming each child item of
573
+ this directory and its descendants. This requires that
574
+ ``D.isdir()``.
575
+
576
+ This performs a depth-first traversal of the directory tree.
577
+ Each directory is returned just before all its children.
578
+
579
+ The `errors=` keyword argument controls behavior when an
580
+ error occurs. The default is ``'strict'``, which causes an
581
+ exception. Other allowed values are ``'warn'`` (which
582
+ reports the error via :func:`warnings.warn()`), and ``'ignore'``.
583
+ `errors` may also be an arbitrary callable taking a msg parameter.
584
+ """
585
+ class Handlers:
586
+ def strict(msg):
587
+ raise
588
+
589
+ def warn(msg):
590
+ warnings.warn(msg, TreeWalkWarning)
591
+
592
+ def ignore(msg):
593
+ pass
594
+
595
+ if not callable(errors) and errors not in vars(Handlers):
596
+ raise ValueError("invalid errors parameter")
597
+ errors = vars(Handlers).get(errors, errors)
598
+
599
+ try:
600
+ childList = self.listdir()
601
+ except Exception:
602
+ exc = sys.exc_info()[1]
603
+ tmpl = "Unable to list directory '%(self)s': %(exc)s"
604
+ msg = tmpl % locals()
605
+ errors(msg)
606
+ return
607
+
608
+ for child in childList:
609
+ if pattern is None or child.fnmatch(pattern):
610
+ yield child
611
+ try:
612
+ isdir = child.isdir()
613
+ except Exception:
614
+ exc = sys.exc_info()[1]
615
+ tmpl = "Unable to access '%(child)s': %(exc)s"
616
+ msg = tmpl % locals()
617
+ errors(msg)
618
+ isdir = False
619
+
620
+ if isdir:
621
+ for item in child.walk(pattern, errors):
622
+ yield item
623
+
624
+ def walkdirs(self, pattern=None, errors='strict'):
625
+ """ D.walkdirs() -> iterator over subdirs, recursively.
626
+
627
+ With the optional `pattern` argument, this yields only
628
+ directories whose names match the given pattern. For
629
+ example, ``mydir.walkdirs('*test')`` yields only directories
630
+ with names ending in ``'test'``.
631
+
632
+ The `errors=` keyword argument controls behavior when an
633
+ error occurs. The default is ``'strict'``, which causes an
634
+ exception. The other allowed values are ``'warn'`` (which
635
+ reports the error via :func:`warnings.warn()`), and ``'ignore'``.
636
+ """
637
+ if errors not in ('strict', 'warn', 'ignore'):
638
+ raise ValueError("invalid errors parameter")
639
+
640
+ try:
641
+ dirs = self.dirs()
642
+ except Exception:
643
+ if errors == 'ignore':
644
+ return
645
+ elif errors == 'warn':
646
+ warnings.warn(
647
+ "Unable to list directory '%s': %s"
648
+ % (self, sys.exc_info()[1]),
649
+ TreeWalkWarning)
650
+ return
651
+ else:
652
+ raise
653
+
654
+ for child in dirs:
655
+ if pattern is None or child.fnmatch(pattern):
656
+ yield child
657
+ for subsubdir in child.walkdirs(pattern, errors):
658
+ yield subsubdir
659
+
660
+ def walkfiles(self, pattern=None, errors='strict'):
661
+ """ D.walkfiles() -> iterator over files in D, recursively.
662
+
663
+ The optional argument `pattern` limits the results to files
664
+ with names that match the pattern. For example,
665
+ ``mydir.walkfiles('*.tmp')`` yields only files with the ``.tmp``
666
+ extension.
667
+ """
668
+ if errors not in ('strict', 'warn', 'ignore'):
669
+ raise ValueError("invalid errors parameter")
670
+
671
+ try:
672
+ childList = self.listdir()
673
+ except Exception:
674
+ if errors == 'ignore':
675
+ return
676
+ elif errors == 'warn':
677
+ warnings.warn(
678
+ "Unable to list directory '%s': %s"
679
+ % (self, sys.exc_info()[1]),
680
+ TreeWalkWarning)
681
+ return
682
+ else:
683
+ raise
684
+
685
+ for child in childList:
686
+ try:
687
+ isfile = child.isfile()
688
+ isdir = not isfile and child.isdir()
689
+ except:
690
+ if errors == 'ignore':
691
+ continue
692
+ elif errors == 'warn':
693
+ warnings.warn(
694
+ "Unable to access '%s': %s"
695
+ % (self, sys.exc_info()[1]),
696
+ TreeWalkWarning)
697
+ continue
698
+ else:
699
+ raise
700
+
701
+ if isfile:
702
+ if pattern is None or child.fnmatch(pattern):
703
+ yield child
704
+ elif isdir:
705
+ for f in child.walkfiles(pattern, errors):
706
+ yield f
707
+
708
+ def fnmatch(self, pattern, normcase=None):
709
+ """ Return ``True`` if `self.name` matches the given `pattern`.
710
+
711
+ `pattern` - A filename pattern with wildcards,
712
+ for example ``'*.py'``. If the pattern contains a `normcase`
713
+ attribute, it is applied to the name and path prior to comparison.
714
+
715
+ `normcase` - (optional) A function used to normalize the pattern and
716
+ filename before matching. Defaults to :meth:`self.module`, which defaults
717
+ to :meth:`os.path.normcase`.
718
+
719
+ .. seealso:: :func:`fnmatch.fnmatch`
720
+ """
721
+ default_normcase = getattr(pattern, 'normcase', self.module.normcase)
722
+ normcase = normcase or default_normcase
723
+ name = normcase(self.name)
724
+ pattern = normcase(pattern)
725
+ return fnmatch.fnmatchcase(name, pattern)
726
+
727
+ def glob(self, pattern):
728
+ """ Return a list of Path objects that match the pattern.
729
+
730
+ `pattern` - a path relative to this directory, with wildcards.
731
+
732
+ For example, ``Path('/users').glob('*/bin/*')`` returns a list
733
+ of all the files users have in their :file:`bin` directories.
734
+
735
+ .. seealso:: :func:`glob.glob`
736
+ """
737
+ cls = self._next_class
738
+ return [cls(s) for s in glob.glob(self / pattern)]
739
+
740
+ #
741
+ # --- Reading or writing an entire file at once.
742
+
743
+ def open(self, *args, **kwargs):
744
+ """ Open this file and return a corresponding :class:`file` object.
745
+
746
+ Keyword arguments work as in :func:`io.open`. If the file cannot be
747
+ opened, an :class:`~exceptions.OSError` is raised.
748
+ """
749
+ with io_error_compat():
750
+ return io.open(self, *args, **kwargs)
751
+
752
+ def bytes(self):
753
+ """ Open this file, read all bytes, return them as a string. """
754
+ with self.open('rb') as f:
755
+ return f.read()
756
+
757
+ def chunks(self, size, *args, **kwargs):
758
+ """ Returns a generator yielding chunks of the file, so it can
759
+ be read piece by piece with a simple for loop.
760
+
761
+ Any argument you pass after `size` will be passed to :meth:`open`.
762
+
763
+ :example:
764
+
765
+ >>> hash = hashlib.md5()
766
+ >>> for chunk in Path("path.py").chunks(8192, mode='rb'):
767
+ ... hash.update(chunk)
768
+
769
+ This will read the file by chunks of 8192 bytes.
770
+ """
771
+ with self.open(*args, **kwargs) as f:
772
+ for chunk in iter(lambda: f.read(size) or None, None):
773
+ yield chunk
774
+
775
+ def write_bytes(self, bytes, append=False):
776
+ """ Open this file and write the given bytes to it.
777
+
778
+ Default behavior is to overwrite any existing file.
779
+ Call ``p.write_bytes(bytes, append=True)`` to append instead.
780
+ """
781
+ if append:
782
+ mode = 'ab'
783
+ else:
784
+ mode = 'wb'
785
+ with self.open(mode) as f:
786
+ f.write(bytes)
787
+
788
+ def text(self, encoding=None, errors='strict'):
789
+ r""" Open this file, read it in, return the content as a string.
790
+
791
+ All newline sequences are converted to ``'\n'``. Keyword arguments
792
+ will be passed to :meth:`open`.
793
+
794
+ .. seealso:: :meth:`lines`
795
+ """
796
+ with self.open(mode='r', encoding=encoding, errors=errors) as f:
797
+ return U_NEWLINE.sub('\n', f.read())
798
+
799
+ def write_text(self, text, encoding=None, errors='strict',
800
+ linesep=os.linesep, append=False):
801
+ r""" Write the given text to this file.
802
+
803
+ The default behavior is to overwrite any existing file;
804
+ to append instead, use the `append=True` keyword argument.
805
+
806
+ There are two differences between :meth:`write_text` and
807
+ :meth:`write_bytes`: newline handling and Unicode handling.
808
+ See below.
809
+
810
+ Parameters:
811
+
812
+ `text` - str/unicode - The text to be written.
813
+
814
+ `encoding` - str - The Unicode encoding that will be used.
815
+ This is ignored if `text` isn't a Unicode string.
816
+
817
+ `errors` - str - How to handle Unicode encoding errors.
818
+ Default is ``'strict'``. See ``help(unicode.encode)`` for the
819
+ options. This is ignored if `text` isn't a Unicode
820
+ string.
821
+
822
+ `linesep` - keyword argument - str/unicode - The sequence of
823
+ characters to be used to mark end-of-line. The default is
824
+ :data:`os.linesep`. You can also specify ``None`` to
825
+ leave all newlines as they are in `text`.
826
+
827
+ `append` - keyword argument - bool - Specifies what to do if
828
+ the file already exists (``True``: append to the end of it;
829
+ ``False``: overwrite it.) The default is ``False``.
830
+
831
+
832
+ --- Newline handling.
833
+
834
+ ``write_text()`` converts all standard end-of-line sequences
835
+ (``'\n'``, ``'\r'``, and ``'\r\n'``) to your platform's default
836
+ end-of-line sequence (see :data:`os.linesep`; on Windows, for example,
837
+ the end-of-line marker is ``'\r\n'``).
838
+
839
+ If you don't like your platform's default, you can override it
840
+ using the `linesep=` keyword argument. If you specifically want
841
+ ``write_text()`` to preserve the newlines as-is, use ``linesep=None``.
842
+
843
+ This applies to Unicode text the same as to 8-bit text, except
844
+ there are three additional standard Unicode end-of-line sequences:
845
+ ``u'\x85'``, ``u'\r\x85'``, and ``u'\u2028'``.
846
+
847
+ (This is slightly different from when you open a file for
848
+ writing with ``fopen(filename, "w")`` in C or ``open(filename, 'w')``
849
+ in Python.)
850
+
851
+
852
+ --- Unicode
853
+
854
+ If `text` isn't Unicode, then apart from newline handling, the
855
+ bytes are written verbatim to the file. The `encoding` and
856
+ `errors` arguments are not used and must be omitted.
857
+
858
+ If `text` is Unicode, it is first converted to :func:`bytes` using the
859
+ specified `encoding` (or the default encoding if `encoding`
860
+ isn't specified). The `errors` argument applies only to this
861
+ conversion.
862
+
863
+ """
864
+ if isinstance(text, text_type):
865
+ if linesep is not None:
866
+ text = U_NEWLINE.sub(linesep, text)
867
+ text = text.encode(encoding or sys.getdefaultencoding(), errors)
868
+ else:
869
+ assert encoding is None
870
+ text = NEWLINE.sub(linesep, text)
871
+ self.write_bytes(text, append=append)
872
+
873
+ def lines(self, encoding=None, errors='strict', retain=True):
874
+ r""" Open this file, read all lines, return them in a list.
875
+
876
+ Optional arguments:
877
+ `encoding` - The Unicode encoding (or character set) of
878
+ the file. The default is ``None``, meaning the content
879
+ of the file is read as 8-bit characters and returned
880
+ as a list of (non-Unicode) str objects.
881
+ `errors` - How to handle Unicode errors; see help(str.decode)
882
+ for the options. Default is ``'strict'``.
883
+ `retain` - If ``True``, retain newline characters; but all newline
884
+ character combinations (``'\r'``, ``'\n'``, ``'\r\n'``) are
885
+ translated to ``'\n'``. If ``False``, newline characters are
886
+ stripped off. Default is ``True``.
887
+
888
+ This uses ``'U'`` mode.
889
+
890
+ .. seealso:: :meth:`text`
891
+ """
892
+ if encoding is None and retain:
893
+ with self.open('U') as f:
894
+ return f.readlines()
895
+ else:
896
+ return self.text(encoding, errors).splitlines(retain)
897
+
898
+ def write_lines(self, lines, encoding=None, errors='strict',
899
+ linesep=os.linesep, append=False):
900
+ r""" Write the given lines of text to this file.
901
+
902
+ By default this overwrites any existing file at this path.
903
+
904
+ This puts a platform-specific newline sequence on every line.
905
+ See `linesep` below.
906
+
907
+ `lines` - A list of strings.
908
+
909
+ `encoding` - A Unicode encoding to use. This applies only if
910
+ `lines` contains any Unicode strings.
911
+
912
+ `errors` - How to handle errors in Unicode encoding. This
913
+ also applies only to Unicode strings.
914
+
915
+ linesep - The desired line-ending. This line-ending is
916
+ applied to every line. If a line already has any
917
+ standard line ending (``'\r'``, ``'\n'``, ``'\r\n'``,
918
+ ``u'\x85'``, ``u'\r\x85'``, ``u'\u2028'``), that will
919
+ be stripped off and this will be used instead. The
920
+ default is os.linesep, which is platform-dependent
921
+ (``'\r\n'`` on Windows, ``'\n'`` on Unix, etc.).
922
+ Specify ``None`` to write the lines as-is, like
923
+ :meth:`file.writelines`.
924
+
925
+ Use the keyword argument ``append=True`` to append lines to the
926
+ file. The default is to overwrite the file.
927
+
928
+ .. warning ::
929
+
930
+ When you use this with Unicode data, if the encoding of the
931
+ existing data in the file is different from the encoding
932
+ you specify with the `encoding=` parameter, the result is
933
+ mixed-encoding data, which can really confuse someone trying
934
+ to read the file later.
935
+ """
936
+ with self.open('ab' if append else 'wb') as f:
937
+ for l in lines:
938
+ isUnicode = isinstance(l, text_type)
939
+ if linesep is not None:
940
+ pattern = U_NL_END if isUnicode else NL_END
941
+ l = pattern.sub('', l) + linesep
942
+ if isUnicode:
943
+ l = l.encode(encoding or sys.getdefaultencoding(), errors)
944
+ f.write(l)
945
+
946
+ def read_md5(self):
947
+ """ Calculate the md5 hash for this file.
948
+
949
+ This reads through the entire file.
950
+
951
+ .. seealso:: :meth:`read_hash`
952
+ """
953
+ return self.read_hash('md5')
954
+
955
+ def _hash(self, hash_name):
956
+ """ Returns a hash object for the file at the current path.
957
+
958
+ `hash_name` should be a hash algo name (such as ``'md5'`` or ``'sha1'``)
959
+ that's available in the :mod:`hashlib` module.
960
+ """
961
+ m = hashlib.new(hash_name)
962
+ for chunk in self.chunks(8192, mode="rb"):
963
+ m.update(chunk)
964
+ return m
965
+
966
+ def read_hash(self, hash_name):
967
+ """ Calculate given hash for this file.
968
+
969
+ List of supported hashes can be obtained from :mod:`hashlib` package.
970
+ This reads the entire file.
971
+
972
+ .. seealso:: :meth:`hashlib.hash.digest`
973
+ """
974
+ return self._hash(hash_name).digest()
975
+
976
+ def read_hexhash(self, hash_name):
977
+ """ Calculate given hash for this file, returning hexdigest.
978
+
979
+ List of supported hashes can be obtained from :mod:`hashlib` package.
980
+ This reads the entire file.
981
+
982
+ .. seealso:: :meth:`hashlib.hash.hexdigest`
983
+ """
984
+ return self._hash(hash_name).hexdigest()
985
+
986
+ # --- Methods for querying the filesystem.
987
+ # N.B. On some platforms, the os.path functions may be implemented in C
988
+ # (e.g. isdir on Windows, Python 3.2.2), and compiled functions don't get
989
+ # bound. Playing it safe and wrapping them all in method calls.
990
+
991
+ def isabs(self):
992
+ """ .. seealso:: :func:`os.path.isabs` """
993
+ return self.module.isabs(self)
994
+
995
+ def exists(self):
996
+ """ .. seealso:: :func:`os.path.exists` """
997
+ return self.module.exists(self)
998
+
999
+ def isdir(self):
1000
+ """ .. seealso:: :func:`os.path.isdir` """
1001
+ return self.module.isdir(self)
1002
+
1003
+ def isfile(self):
1004
+ """ .. seealso:: :func:`os.path.isfile` """
1005
+ return self.module.isfile(self)
1006
+
1007
+ def islink(self):
1008
+ """ .. seealso:: :func:`os.path.islink` """
1009
+ return self.module.islink(self)
1010
+
1011
+ def ismount(self):
1012
+ """ .. seealso:: :func:`os.path.ismount` """
1013
+ return self.module.ismount(self)
1014
+
1015
+ def samefile(self, other):
1016
+ """ .. seealso:: :func:`os.path.samefile` """
1017
+ if not hasattr(self.module, 'samefile'):
1018
+ other = Path(other).realpath().normpath().normcase()
1019
+ return self.realpath().normpath().normcase() == other
1020
+ return self.module.samefile(self, other)
1021
+
1022
+ def getatime(self):
1023
+ """ .. seealso:: :attr:`atime`, :func:`os.path.getatime` """
1024
+ return self.module.getatime(self)
1025
+
1026
+ atime = property(
1027
+ getatime, None, None,
1028
+ """ Last access time of the file.
1029
+
1030
+ .. seealso:: :meth:`getatime`, :func:`os.path.getatime`
1031
+ """)
1032
+
1033
+ def getmtime(self):
1034
+ """ .. seealso:: :attr:`mtime`, :func:`os.path.getmtime` """
1035
+ return self.module.getmtime(self)
1036
+
1037
+ mtime = property(
1038
+ getmtime, None, None,
1039
+ """ Last-modified time of the file.
1040
+
1041
+ .. seealso:: :meth:`getmtime`, :func:`os.path.getmtime`
1042
+ """)
1043
+
1044
+ def getctime(self):
1045
+ """ .. seealso:: :attr:`ctime`, :func:`os.path.getctime` """
1046
+ return self.module.getctime(self)
1047
+
1048
+ ctime = property(
1049
+ getctime, None, None,
1050
+ """ Creation time of the file.
1051
+
1052
+ .. seealso:: :meth:`getctime`, :func:`os.path.getctime`
1053
+ """)
1054
+
1055
+ def getsize(self):
1056
+ """ .. seealso:: :attr:`size`, :func:`os.path.getsize` """
1057
+ return self.module.getsize(self)
1058
+
1059
+ size = property(
1060
+ getsize, None, None,
1061
+ """ Size of the file, in bytes.
1062
+
1063
+ .. seealso:: :meth:`getsize`, :func:`os.path.getsize`
1064
+ """)
1065
+
1066
+ if hasattr(os, 'access'):
1067
+ def access(self, mode):
1068
+ """ Return ``True`` if current user has access to this path.
1069
+
1070
+ mode - One of the constants :data:`os.F_OK`, :data:`os.R_OK`,
1071
+ :data:`os.W_OK`, :data:`os.X_OK`
1072
+
1073
+ .. seealso:: :func:`os.access`
1074
+ """
1075
+ return os.access(self, mode)
1076
+
1077
+ def stat(self):
1078
+ """ Perform a ``stat()`` system call on this path.
1079
+
1080
+ .. seealso:: :meth:`lstat`, :func:`os.stat`
1081
+ """
1082
+ return os.stat(self)
1083
+
1084
+ def lstat(self):
1085
+ """ Like :meth:`stat`, but do not follow symbolic links.
1086
+
1087
+ .. seealso:: :meth:`stat`, :func:`os.lstat`
1088
+ """
1089
+ return os.lstat(self)
1090
+
1091
+ def __get_owner_windows(self):
1092
+ """
1093
+ Return the name of the owner of this file or directory. Follow
1094
+ symbolic links.
1095
+
1096
+ Return a name of the form ``r'DOMAIN\\User Name'``; may be a group.
1097
+
1098
+ .. seealso:: :attr:`owner`
1099
+ """
1100
+ desc = win32security.GetFileSecurity(
1101
+ self, win32security.OWNER_SECURITY_INFORMATION)
1102
+ sid = desc.GetSecurityDescriptorOwner()
1103
+ account, domain, typecode = win32security.LookupAccountSid(None, sid)
1104
+ return domain + '\\' + account
1105
+
1106
+ def __get_owner_unix(self):
1107
+ """
1108
+ Return the name of the owner of this file or directory. Follow
1109
+ symbolic links.
1110
+
1111
+ .. seealso:: :attr:`owner`
1112
+ """
1113
+ st = self.stat()
1114
+ return pwd.getpwuid(st.st_uid).pw_name
1115
+
1116
+ def __get_owner_not_implemented(self):
1117
+ raise NotImplementedError("Ownership not available on this platform.")
1118
+
1119
+ if 'win32security' in globals():
1120
+ get_owner = __get_owner_windows
1121
+ elif 'pwd' in globals():
1122
+ get_owner = __get_owner_unix
1123
+ else:
1124
+ get_owner = __get_owner_not_implemented
1125
+
1126
+ owner = property(
1127
+ get_owner, None, None,
1128
+ """ Name of the owner of this file or directory.
1129
+
1130
+ .. seealso:: :meth:`get_owner`""")
1131
+
1132
+ if hasattr(os, 'statvfs'):
1133
+ def statvfs(self):
1134
+ """ Perform a ``statvfs()`` system call on this path.
1135
+
1136
+ .. seealso:: :func:`os.statvfs`
1137
+ """
1138
+ return os.statvfs(self)
1139
+
1140
+ if hasattr(os, 'pathconf'):
1141
+ def pathconf(self, name):
1142
+ """ .. seealso:: :func:`os.pathconf` """
1143
+ return os.pathconf(self, name)
1144
+
1145
+ #
1146
+ # --- Modifying operations on files and directories
1147
+
1148
+ def utime(self, times):
1149
+ """ Set the access and modified times of this file.
1150
+
1151
+ .. seealso:: :func:`os.utime`
1152
+ """
1153
+ os.utime(self, times)
1154
+ return self
1155
+
1156
+ def chmod(self, mode):
1157
+ """
1158
+ Set the mode. May be the new mode (os.chmod behavior) or a `symbolic
1159
+ mode <http://en.wikipedia.org/wiki/Chmod#Symbolic_modes>`_.
1160
+
1161
+ .. seealso:: :func:`os.chmod`
1162
+ """
1163
+ if isinstance(mode, string_types):
1164
+ mask = _multi_permission_mask(mode)
1165
+ mode = mask(self.stat().st_mode)
1166
+ os.chmod(self, mode)
1167
+ return self
1168
+
1169
+ def chown(self, uid=-1, gid=-1):
1170
+ """
1171
+ Change the owner and group by names rather than the uid or gid numbers.
1172
+
1173
+ .. seealso:: :func:`os.chown`
1174
+ """
1175
+ if hasattr(os, 'chown'):
1176
+ if 'pwd' in globals() and isinstance(uid, string_types):
1177
+ uid = pwd.getpwnam(uid).pw_uid
1178
+ if 'grp' in globals() and isinstance(gid, string_types):
1179
+ gid = grp.getgrnam(gid).gr_gid
1180
+ os.chown(self, uid, gid)
1181
+ else:
1182
+ raise NotImplementedError("Ownership not available on this platform.")
1183
+ return self
1184
+
1185
+ def rename(self, new):
1186
+ """ .. seealso:: :func:`os.rename` """
1187
+ os.rename(self, new)
1188
+ return self._next_class(new)
1189
+
1190
+ def renames(self, new):
1191
+ """ .. seealso:: :func:`os.renames` """
1192
+ os.renames(self, new)
1193
+ return self._next_class(new)
1194
+
1195
+ #
1196
+ # --- Create/delete operations on directories
1197
+
1198
+ def mkdir(self, mode=0o777):
1199
+ """ .. seealso:: :func:`os.mkdir` """
1200
+ os.mkdir(self, mode)
1201
+ return self
1202
+
1203
+ def mkdir_p(self, mode=0o777):
1204
+ """ Like :meth:`mkdir`, but does not raise an exception if the
1205
+ directory already exists. """
1206
+ try:
1207
+ self.mkdir(mode)
1208
+ except OSError:
1209
+ _, e, _ = sys.exc_info()
1210
+ if e.errno != errno.EEXIST:
1211
+ raise
1212
+ return self
1213
+
1214
+ def makedirs(self, mode=0o777):
1215
+ """ .. seealso:: :func:`os.makedirs` """
1216
+ os.makedirs(self, mode)
1217
+ return self
1218
+
1219
+ def makedirs_p(self, mode=0o777):
1220
+ """ Like :meth:`makedirs`, but does not raise an exception if the
1221
+ directory already exists. """
1222
+ try:
1223
+ self.makedirs(mode)
1224
+ except OSError:
1225
+ _, e, _ = sys.exc_info()
1226
+ if e.errno != errno.EEXIST:
1227
+ raise
1228
+ return self
1229
+
1230
+ def rmdir(self):
1231
+ """ .. seealso:: :func:`os.rmdir` """
1232
+ os.rmdir(self)
1233
+ return self
1234
+
1235
+ def rmdir_p(self):
1236
+ """ Like :meth:`rmdir`, but does not raise an exception if the
1237
+ directory is not empty or does not exist. """
1238
+ try:
1239
+ self.rmdir()
1240
+ except OSError:
1241
+ _, e, _ = sys.exc_info()
1242
+ if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST:
1243
+ raise
1244
+ return self
1245
+
1246
+ def removedirs(self):
1247
+ """ .. seealso:: :func:`os.removedirs` """
1248
+ os.removedirs(self)
1249
+ return self
1250
+
1251
+ def removedirs_p(self):
1252
+ """ Like :meth:`removedirs`, but does not raise an exception if the
1253
+ directory is not empty or does not exist. """
1254
+ try:
1255
+ self.removedirs()
1256
+ except OSError:
1257
+ _, e, _ = sys.exc_info()
1258
+ if e.errno != errno.ENOTEMPTY and e.errno != errno.EEXIST:
1259
+ raise
1260
+ return self
1261
+
1262
+ # --- Modifying operations on files
1263
+
1264
+ def touch(self):
1265
+ """ Set the access/modified times of this file to the current time.
1266
+ Create the file if it does not exist.
1267
+ """
1268
+ fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0o666)
1269
+ os.close(fd)
1270
+ os.utime(self, None)
1271
+ return self
1272
+
1273
+ def remove(self):
1274
+ """ .. seealso:: :func:`os.remove` """
1275
+ os.remove(self)
1276
+ return self
1277
+
1278
+ def remove_p(self):
1279
+ """ Like :meth:`remove`, but does not raise an exception if the
1280
+ file does not exist. """
1281
+ try:
1282
+ self.unlink()
1283
+ except OSError:
1284
+ _, e, _ = sys.exc_info()
1285
+ if e.errno != errno.ENOENT:
1286
+ raise
1287
+ return self
1288
+
1289
+ def unlink(self):
1290
+ """ .. seealso:: :func:`os.unlink` """
1291
+ os.unlink(self)
1292
+ return self
1293
+
1294
+ def unlink_p(self):
1295
+ """ Like :meth:`unlink`, but does not raise an exception if the
1296
+ file does not exist. """
1297
+ self.remove_p()
1298
+ return self
1299
+
1300
+ # --- Links
1301
+
1302
+ if hasattr(os, 'link'):
1303
+ def link(self, newpath):
1304
+ """ Create a hard link at `newpath`, pointing to this file.
1305
+
1306
+ .. seealso:: :func:`os.link`
1307
+ """
1308
+ os.link(self, newpath)
1309
+ return self._next_class(newpath)
1310
+
1311
+ if hasattr(os, 'symlink'):
1312
+ def symlink(self, newlink):
1313
+ """ Create a symbolic link at `newlink`, pointing here.
1314
+
1315
+ .. seealso:: :func:`os.symlink`
1316
+ """
1317
+ os.symlink(self, newlink)
1318
+ return self._next_class(newlink)
1319
+
1320
+ if hasattr(os, 'readlink'):
1321
+ def readlink(self):
1322
+ """ Return the path to which this symbolic link points.
1323
+
1324
+ The result may be an absolute or a relative path.
1325
+
1326
+ .. seealso:: :meth:`readlinkabs`, :func:`os.readlink`
1327
+ """
1328
+ return self._next_class(os.readlink(self))
1329
+
1330
+ def readlinkabs(self):
1331
+ """ Return the path to which this symbolic link points.
1332
+
1333
+ The result is always an absolute path.
1334
+
1335
+ .. seealso:: :meth:`readlink`, :func:`os.readlink`
1336
+ """
1337
+ p = self.readlink()
1338
+ if p.isabs():
1339
+ return p
1340
+ else:
1341
+ return (self.parent / p).abspath()
1342
+
1343
+ # High-level functions from shutil
1344
+ # These functions will be bound to the instance such that
1345
+ # Path(name).copy(target) will invoke shutil.copy(name, target)
1346
+
1347
+ copyfile = shutil.copyfile
1348
+ copymode = shutil.copymode
1349
+ copystat = shutil.copystat
1350
+ copy = shutil.copy
1351
+ copy2 = shutil.copy2
1352
+ copytree = shutil.copytree
1353
+ if hasattr(shutil, 'move'):
1354
+ move = shutil.move
1355
+ rmtree = shutil.rmtree
1356
+
1357
+ def rmtree_p(self):
1358
+ """ Like :meth:`rmtree`, but does not raise an exception if the
1359
+ directory does not exist. """
1360
+ try:
1361
+ self.rmtree()
1362
+ except OSError:
1363
+ _, e, _ = sys.exc_info()
1364
+ if e.errno != errno.ENOENT:
1365
+ raise
1366
+ return self
1367
+
1368
+ def chdir(self):
1369
+ """ .. seealso:: :func:`os.chdir` """
1370
+ os.chdir(self)
1371
+
1372
+ cd = chdir
1373
+
1374
+ def merge_tree(self, dst, symlinks=False, *args, **kwargs):
1375
+ """
1376
+ Copy entire contents of self to dst, overwriting existing
1377
+ contents in dst with those in self.
1378
+
1379
+ If the additional keyword `update` is True, each
1380
+ `src` will only be copied if `dst` does not exist,
1381
+ or `src` is newer than `dst`.
1382
+
1383
+ Note that the technique employed stages the files in a temporary
1384
+ directory first, so this function is not suitable for merging
1385
+ trees with large files, especially if the temporary directory
1386
+ is not capable of storing a copy of the entire source tree.
1387
+ """
1388
+ update = kwargs.pop('update', False)
1389
+ with tempdir() as _temp_dir:
1390
+ # first copy the tree to a stage directory to support
1391
+ # the parameters and behavior of copytree.
1392
+ stage = _temp_dir / str(hash(self))
1393
+ self.copytree(stage, symlinks, *args, **kwargs)
1394
+ # now copy everything from the stage directory using
1395
+ # the semantics of dir_util.copy_tree
1396
+ dir_util.copy_tree(stage, dst, preserve_symlinks=symlinks,
1397
+ update=update)
1398
+
1399
+ #
1400
+ # --- Special stuff from os
1401
+
1402
+ if hasattr(os, 'chroot'):
1403
+ def chroot(self):
1404
+ """ .. seealso:: :func:`os.chroot` """
1405
+ os.chroot(self)
1406
+
1407
+ if hasattr(os, 'startfile'):
1408
+ def startfile(self):
1409
+ """ .. seealso:: :func:`os.startfile` """
1410
+ os.startfile(self)
1411
+ return self
1412
+
1413
+ # in-place re-writing, courtesy of Martijn Pieters
1414
+ # http://www.zopatista.com/python/2013/11/26/inplace-file-rewriting/
1415
+ @contextlib.contextmanager
1416
+ def in_place(self, mode='r', buffering=-1, encoding=None, errors=None,
1417
+ newline=None, backup_extension=None):
1418
+ """
1419
+ A context in which a file may be re-written in-place with new content.
1420
+
1421
+ Yields a tuple of :samp:`({readable}, {writable})` file objects, where `writable`
1422
+ replaces `readable`.
1423
+
1424
+ If an exception occurs, the old file is restored, removing the
1425
+ written data.
1426
+
1427
+ Mode *must not* use ``'w'``, ``'a'``, or ``'+'``; only read-only-modes are
1428
+ allowed. A :exc:`ValueError` is raised on invalid modes.
1429
+
1430
+ For example, to add line numbers to a file::
1431
+
1432
+ p = Path(filename)
1433
+ assert p.isfile()
1434
+ with p.in_place() as (reader, writer):
1435
+ for number, line in enumerate(reader, 1):
1436
+ writer.write('{0:3}: '.format(number)))
1437
+ writer.write(line)
1438
+
1439
+ Thereafter, the file at `filename` will have line numbers in it.
1440
+ """
1441
+ import io
1442
+
1443
+ if set(mode).intersection('wa+'):
1444
+ raise ValueError('Only read-only file modes can be used')
1445
+
1446
+ # move existing file to backup, create new file with same permissions
1447
+ # borrowed extensively from the fileinput module
1448
+ backup_fn = self + (backup_extension or os.extsep + 'bak')
1449
+ try:
1450
+ os.unlink(backup_fn)
1451
+ except os.error:
1452
+ pass
1453
+ os.rename(self, backup_fn)
1454
+ readable = io.open(backup_fn, mode, buffering=buffering,
1455
+ encoding=encoding, errors=errors, newline=newline)
1456
+ try:
1457
+ perm = os.fstat(readable.fileno()).st_mode
1458
+ except OSError:
1459
+ writable = open(self, 'w' + mode.replace('r', ''),
1460
+ buffering=buffering, encoding=encoding, errors=errors,
1461
+ newline=newline)
1462
+ else:
1463
+ os_mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
1464
+ if hasattr(os, 'O_BINARY'):
1465
+ os_mode |= os.O_BINARY
1466
+ fd = os.open(self, os_mode, perm)
1467
+ writable = io.open(fd, "w" + mode.replace('r', ''),
1468
+ buffering=buffering, encoding=encoding, errors=errors,
1469
+ newline=newline)
1470
+ try:
1471
+ if hasattr(os, 'chmod'):
1472
+ os.chmod(self, perm)
1473
+ except OSError:
1474
+ pass
1475
+ try:
1476
+ yield readable, writable
1477
+ except Exception:
1478
+ # move backup back
1479
+ readable.close()
1480
+ writable.close()
1481
+ try:
1482
+ os.unlink(self)
1483
+ except os.error:
1484
+ pass
1485
+ os.rename(backup_fn, self)
1486
+ raise
1487
+ else:
1488
+ readable.close()
1489
+ writable.close()
1490
+ finally:
1491
+ try:
1492
+ os.unlink(backup_fn)
1493
+ except os.error:
1494
+ pass
1495
+
1496
+ @ClassProperty
1497
+ @classmethod
1498
+ def special(cls):
1499
+ """
1500
+ Return a SpecialResolver object suitable referencing a suitable
1501
+ directory for the relevant platform for the given
1502
+ type of content.
1503
+
1504
+ For example, to get a user config directory, invoke:
1505
+
1506
+ dir = Path.special().user.config
1507
+
1508
+ Uses the `appdirs
1509
+ <https://pypi.python.org/pypi/appdirs/1.4.0>`_ to resolve
1510
+ the paths in a platform-friendly way.
1511
+
1512
+ To create a config directory for 'My App', consider:
1513
+
1514
+ dir = Path.special("My App").user.config.makedirs_p()
1515
+
1516
+ If the ``appdirs`` module is not installed, invocation
1517
+ of special will raise an ImportError.
1518
+ """
1519
+ return functools.partial(SpecialResolver, cls)
1520
+
1521
+
1522
+ class SpecialResolver(object):
1523
+ class ResolverScope:
1524
+ def __init__(self, paths, scope):
1525
+ self.paths = paths
1526
+ self.scope = scope
1527
+
1528
+ def __getattr__(self, class_):
1529
+ return self.paths.get_dir(self.scope, class_)
1530
+
1531
+ def __init__(self, path_class, *args, **kwargs):
1532
+ appdirs = importlib.import_module('appdirs')
1533
+
1534
+ # let appname default to None until
1535
+ # https://github.com/ActiveState/appdirs/issues/55 is solved.
1536
+ not args and kwargs.setdefault('appname', None)
1537
+
1538
+ vars(self).update(
1539
+ path_class=path_class,
1540
+ wrapper=appdirs.AppDirs(*args, **kwargs),
1541
+ )
1542
+
1543
+ def __getattr__(self, scope):
1544
+ return self.ResolverScope(self, scope)
1545
+
1546
+ def get_dir(self, scope, class_):
1547
+ """
1548
+ Return the callable function from appdirs, but with the
1549
+ result wrapped in self.path_class
1550
+ """
1551
+ prop_name = '{scope}_{class_}_dir'.format(**locals())
1552
+ value = getattr(self.wrapper, prop_name)
1553
+ MultiPath = Multi.for_class(self.path_class)
1554
+ return MultiPath.detect(value)
1555
+
1556
+
1557
+ class Multi:
1558
+ """
1559
+ A mix-in for a Path which may contain multiple Path separated by pathsep.
1560
+ """
1561
+ @classmethod
1562
+ def for_class(cls, path_cls):
1563
+ name = 'Multi' + path_cls.__name__
1564
+ if PY2:
1565
+ name = str(name)
1566
+ return type(name, (cls, path_cls), {})
1567
+
1568
+ @classmethod
1569
+ def detect(cls, input):
1570
+ if os.pathsep not in input:
1571
+ cls = cls._next_class
1572
+ return cls(input)
1573
+
1574
+ def __iter__(self):
1575
+ return iter(map(self._next_class, self.split(os.pathsep)))
1576
+
1577
+ @ClassProperty
1578
+ @classmethod
1579
+ def _next_class(cls):
1580
+ """
1581
+ Multi-subclasses should use the parent class
1582
+ """
1583
+ return next(
1584
+ class_
1585
+ for class_ in cls.__mro__
1586
+ if not issubclass(class_, Multi)
1587
+ )
1588
+
1589
+
1590
+ class tempdir(Path):
1591
+ """
1592
+ A temporary directory via :func:`tempfile.mkdtemp`, and constructed with the
1593
+ same parameters that you can use as a context manager.
1594
+
1595
+ Example:
1596
+
1597
+ with tempdir() as d:
1598
+ # do stuff with the Path object "d"
1599
+
1600
+ # here the directory is deleted automatically
1601
+
1602
+ .. seealso:: :func:`tempfile.mkdtemp`
1603
+ """
1604
+
1605
+ @ClassProperty
1606
+ @classmethod
1607
+ def _next_class(cls):
1608
+ return Path
1609
+
1610
+ def __new__(cls, *args, **kwargs):
1611
+ dirname = tempfile.mkdtemp(*args, **kwargs)
1612
+ return super(tempdir, cls).__new__(cls, dirname)
1613
+
1614
+ def __init__(self, *args, **kwargs):
1615
+ pass
1616
+
1617
+ def __enter__(self):
1618
+ return self
1619
+
1620
+ def __exit__(self, exc_type, exc_value, traceback):
1621
+ if not exc_value:
1622
+ self.rmtree()
1623
+
1624
+
1625
+ def _multi_permission_mask(mode):
1626
+ """
1627
+ Support multiple, comma-separated Unix chmod symbolic modes.
1628
+
1629
+ >>> _multi_permission_mask('a=r,u+w')(0) == 0o644
1630
+ True
1631
+ """
1632
+ compose = lambda f, g: lambda *args, **kwargs: g(f(*args, **kwargs))
1633
+ return functools.reduce(compose, map(_permission_mask, mode.split(',')))
1634
+
1635
+
1636
+ def _permission_mask(mode):
1637
+ """
1638
+ Convert a Unix chmod symbolic mode like ``'ugo+rwx'`` to a function
1639
+ suitable for applying to a mask to affect that change.
1640
+
1641
+ >>> mask = _permission_mask('ugo+rwx')
1642
+ >>> mask(0o554) == 0o777
1643
+ True
1644
+
1645
+ >>> _permission_mask('go-x')(0o777) == 0o766
1646
+ True
1647
+
1648
+ >>> _permission_mask('o-x')(0o445) == 0o444
1649
+ True
1650
+
1651
+ >>> _permission_mask('a+x')(0) == 0o111
1652
+ True
1653
+
1654
+ >>> _permission_mask('a=rw')(0o057) == 0o666
1655
+ True
1656
+
1657
+ >>> _permission_mask('u=x')(0o666) == 0o166
1658
+ True
1659
+
1660
+ >>> _permission_mask('g=')(0o157) == 0o107
1661
+ True
1662
+ """
1663
+ # parse the symbolic mode
1664
+ parsed = re.match('(?P<who>[ugoa]+)(?P<op>[-+=])(?P<what>[rwx]*)$', mode)
1665
+ if not parsed:
1666
+ raise ValueError("Unrecognized symbolic mode", mode)
1667
+
1668
+ # generate a mask representing the specified permission
1669
+ spec_map = dict(r=4, w=2, x=1)
1670
+ specs = (spec_map[perm] for perm in parsed.group('what'))
1671
+ spec = functools.reduce(operator.or_, specs, 0)
1672
+
1673
+ # now apply spec to each subject in who
1674
+ shift_map = dict(u=6, g=3, o=0)
1675
+ who = parsed.group('who').replace('a', 'ugo')
1676
+ masks = (spec << shift_map[subj] for subj in who)
1677
+ mask = functools.reduce(operator.or_, masks)
1678
+
1679
+ op = parsed.group('op')
1680
+
1681
+ # if op is -, invert the mask
1682
+ if op == '-':
1683
+ mask ^= 0o777
1684
+
1685
+ # if op is =, retain extant values for unreferenced subjects
1686
+ if op == '=':
1687
+ masks = (0o7 << shift_map[subj] for subj in who)
1688
+ retain = functools.reduce(operator.or_, masks) ^ 0o777
1689
+
1690
+ op_map = {
1691
+ '+': operator.or_,
1692
+ '-': operator.and_,
1693
+ '=': lambda mask, target: target & retain ^ mask,
1694
+ }
1695
+ return functools.partial(op_map[op], mask)
1696
+
1697
+
1698
+ class CaseInsensitivePattern(text_type):
1699
+ """
1700
+ A string with a ``'normcase'`` property, suitable for passing to
1701
+ :meth:`listdir`, :meth:`dirs`, :meth:`files`, :meth:`walk`,
1702
+ :meth:`walkdirs`, or :meth:`walkfiles` to match case-insensitive.
1703
+
1704
+ For example, to get all files ending in .py, .Py, .pY, or .PY in the
1705
+ current directory::
1706
+
1707
+ from path import Path, CaseInsensitivePattern as ci
1708
+ Path('.').files(ci('*.py'))
1709
+ """
1710
+
1711
+ @property
1712
+ def normcase(self):
1713
+ return __import__('ntpath').normcase
1714
+
1715
+ ########################
1716
+ # Backward-compatibility
1717
+ class path(Path):
1718
+ def __new__(cls, *args, **kwargs):
1719
+ msg = "path is deprecated. Use Path instead."
1720
+ warnings.warn(msg, DeprecationWarning)
1721
+ return Path.__new__(cls, *args, **kwargs)
1722
+
1723
+
1724
+ __all__ += ['path']
1725
+ ########################