types.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. import os
  2. import stat
  3. from datetime import datetime
  4. from ._compat import _get_argv_encoding
  5. from ._compat import filename_to_ui
  6. from ._compat import get_filesystem_encoding
  7. from ._compat import get_streerror
  8. from ._compat import open_stream
  9. from ._compat import PY2
  10. from ._compat import text_type
  11. from .exceptions import BadParameter
  12. from .utils import LazyFile
  13. from .utils import safecall
  14. class ParamType(object):
  15. """Helper for converting values through types. The following is
  16. necessary for a valid type:
  17. * it needs a name
  18. * it needs to pass through None unchanged
  19. * it needs to convert from a string
  20. * it needs to convert its result type through unchanged
  21. (eg: needs to be idempotent)
  22. * it needs to be able to deal with param and context being `None`.
  23. This can be the case when the object is used with prompt
  24. inputs.
  25. """
  26. is_composite = False
  27. #: the descriptive name of this type
  28. name = None
  29. #: if a list of this type is expected and the value is pulled from a
  30. #: string environment variable, this is what splits it up. `None`
  31. #: means any whitespace. For all parameters the general rule is that
  32. #: whitespace splits them up. The exception are paths and files which
  33. #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
  34. #: Windows).
  35. envvar_list_splitter = None
  36. def __call__(self, value, param=None, ctx=None):
  37. if value is not None:
  38. return self.convert(value, param, ctx)
  39. def get_metavar(self, param):
  40. """Returns the metavar default for this param if it provides one."""
  41. def get_missing_message(self, param):
  42. """Optionally might return extra information about a missing
  43. parameter.
  44. .. versionadded:: 2.0
  45. """
  46. def convert(self, value, param, ctx):
  47. """Converts the value. This is not invoked for values that are
  48. `None` (the missing value).
  49. """
  50. return value
  51. def split_envvar_value(self, rv):
  52. """Given a value from an environment variable this splits it up
  53. into small chunks depending on the defined envvar list splitter.
  54. If the splitter is set to `None`, which means that whitespace splits,
  55. then leading and trailing whitespace is ignored. Otherwise, leading
  56. and trailing splitters usually lead to empty items being included.
  57. """
  58. return (rv or "").split(self.envvar_list_splitter)
  59. def fail(self, message, param=None, ctx=None):
  60. """Helper method to fail with an invalid value message."""
  61. raise BadParameter(message, ctx=ctx, param=param)
  62. class CompositeParamType(ParamType):
  63. is_composite = True
  64. @property
  65. def arity(self):
  66. raise NotImplementedError()
  67. class FuncParamType(ParamType):
  68. def __init__(self, func):
  69. self.name = func.__name__
  70. self.func = func
  71. def convert(self, value, param, ctx):
  72. try:
  73. return self.func(value)
  74. except ValueError:
  75. try:
  76. value = text_type(value)
  77. except UnicodeError:
  78. value = str(value).decode("utf-8", "replace")
  79. self.fail(value, param, ctx)
  80. class UnprocessedParamType(ParamType):
  81. name = "text"
  82. def convert(self, value, param, ctx):
  83. return value
  84. def __repr__(self):
  85. return "UNPROCESSED"
  86. class StringParamType(ParamType):
  87. name = "text"
  88. def convert(self, value, param, ctx):
  89. if isinstance(value, bytes):
  90. enc = _get_argv_encoding()
  91. try:
  92. value = value.decode(enc)
  93. except UnicodeError:
  94. fs_enc = get_filesystem_encoding()
  95. if fs_enc != enc:
  96. try:
  97. value = value.decode(fs_enc)
  98. except UnicodeError:
  99. value = value.decode("utf-8", "replace")
  100. else:
  101. value = value.decode("utf-8", "replace")
  102. return value
  103. return value
  104. def __repr__(self):
  105. return "STRING"
  106. class Choice(ParamType):
  107. """The choice type allows a value to be checked against a fixed set
  108. of supported values. All of these values have to be strings.
  109. You should only pass a list or tuple of choices. Other iterables
  110. (like generators) may lead to surprising results.
  111. The resulting value will always be one of the originally passed choices
  112. regardless of ``case_sensitive`` or any ``ctx.token_normalize_func``
  113. being specified.
  114. See :ref:`choice-opts` for an example.
  115. :param case_sensitive: Set to false to make choices case
  116. insensitive. Defaults to true.
  117. """
  118. name = "choice"
  119. def __init__(self, choices, case_sensitive=True):
  120. self.choices = choices
  121. self.case_sensitive = case_sensitive
  122. def get_metavar(self, param):
  123. return "[{}]".format("|".join(self.choices))
  124. def get_missing_message(self, param):
  125. return "Choose from:\n\t{}.".format(",\n\t".join(self.choices))
  126. def convert(self, value, param, ctx):
  127. # Match through normalization and case sensitivity
  128. # first do token_normalize_func, then lowercase
  129. # preserve original `value` to produce an accurate message in
  130. # `self.fail`
  131. normed_value = value
  132. normed_choices = {choice: choice for choice in self.choices}
  133. if ctx is not None and ctx.token_normalize_func is not None:
  134. normed_value = ctx.token_normalize_func(value)
  135. normed_choices = {
  136. ctx.token_normalize_func(normed_choice): original
  137. for normed_choice, original in normed_choices.items()
  138. }
  139. if not self.case_sensitive:
  140. if PY2:
  141. lower = str.lower
  142. else:
  143. lower = str.casefold
  144. normed_value = lower(normed_value)
  145. normed_choices = {
  146. lower(normed_choice): original
  147. for normed_choice, original in normed_choices.items()
  148. }
  149. if normed_value in normed_choices:
  150. return normed_choices[normed_value]
  151. self.fail(
  152. "invalid choice: {}. (choose from {})".format(
  153. value, ", ".join(self.choices)
  154. ),
  155. param,
  156. ctx,
  157. )
  158. def __repr__(self):
  159. return "Choice('{}')".format(list(self.choices))
  160. class DateTime(ParamType):
  161. """The DateTime type converts date strings into `datetime` objects.
  162. The format strings which are checked are configurable, but default to some
  163. common (non-timezone aware) ISO 8601 formats.
  164. When specifying *DateTime* formats, you should only pass a list or a tuple.
  165. Other iterables, like generators, may lead to surprising results.
  166. The format strings are processed using ``datetime.strptime``, and this
  167. consequently defines the format strings which are allowed.
  168. Parsing is tried using each format, in order, and the first format which
  169. parses successfully is used.
  170. :param formats: A list or tuple of date format strings, in the order in
  171. which they should be tried. Defaults to
  172. ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``,
  173. ``'%Y-%m-%d %H:%M:%S'``.
  174. """
  175. name = "datetime"
  176. def __init__(self, formats=None):
  177. self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"]
  178. def get_metavar(self, param):
  179. return "[{}]".format("|".join(self.formats))
  180. def _try_to_convert_date(self, value, format):
  181. try:
  182. return datetime.strptime(value, format)
  183. except ValueError:
  184. return None
  185. def convert(self, value, param, ctx):
  186. # Exact match
  187. for format in self.formats:
  188. dtime = self._try_to_convert_date(value, format)
  189. if dtime:
  190. return dtime
  191. self.fail(
  192. "invalid datetime format: {}. (choose from {})".format(
  193. value, ", ".join(self.formats)
  194. )
  195. )
  196. def __repr__(self):
  197. return "DateTime"
  198. class IntParamType(ParamType):
  199. name = "integer"
  200. def convert(self, value, param, ctx):
  201. try:
  202. return int(value)
  203. except ValueError:
  204. self.fail("{} is not a valid integer".format(value), param, ctx)
  205. def __repr__(self):
  206. return "INT"
  207. class IntRange(IntParamType):
  208. """A parameter that works similar to :data:`click.INT` but restricts
  209. the value to fit into a range. The default behavior is to fail if the
  210. value falls outside the range, but it can also be silently clamped
  211. between the two edges.
  212. See :ref:`ranges` for an example.
  213. """
  214. name = "integer range"
  215. def __init__(self, min=None, max=None, clamp=False):
  216. self.min = min
  217. self.max = max
  218. self.clamp = clamp
  219. def convert(self, value, param, ctx):
  220. rv = IntParamType.convert(self, value, param, ctx)
  221. if self.clamp:
  222. if self.min is not None and rv < self.min:
  223. return self.min
  224. if self.max is not None and rv > self.max:
  225. return self.max
  226. if (
  227. self.min is not None
  228. and rv < self.min
  229. or self.max is not None
  230. and rv > self.max
  231. ):
  232. if self.min is None:
  233. self.fail(
  234. "{} is bigger than the maximum valid value {}.".format(
  235. rv, self.max
  236. ),
  237. param,
  238. ctx,
  239. )
  240. elif self.max is None:
  241. self.fail(
  242. "{} is smaller than the minimum valid value {}.".format(
  243. rv, self.min
  244. ),
  245. param,
  246. ctx,
  247. )
  248. else:
  249. self.fail(
  250. "{} is not in the valid range of {} to {}.".format(
  251. rv, self.min, self.max
  252. ),
  253. param,
  254. ctx,
  255. )
  256. return rv
  257. def __repr__(self):
  258. return "IntRange({}, {})".format(self.min, self.max)
  259. class FloatParamType(ParamType):
  260. name = "float"
  261. def convert(self, value, param, ctx):
  262. try:
  263. return float(value)
  264. except ValueError:
  265. self.fail(
  266. "{} is not a valid floating point value".format(value), param, ctx
  267. )
  268. def __repr__(self):
  269. return "FLOAT"
  270. class FloatRange(FloatParamType):
  271. """A parameter that works similar to :data:`click.FLOAT` but restricts
  272. the value to fit into a range. The default behavior is to fail if the
  273. value falls outside the range, but it can also be silently clamped
  274. between the two edges.
  275. See :ref:`ranges` for an example.
  276. """
  277. name = "float range"
  278. def __init__(self, min=None, max=None, clamp=False):
  279. self.min = min
  280. self.max = max
  281. self.clamp = clamp
  282. def convert(self, value, param, ctx):
  283. rv = FloatParamType.convert(self, value, param, ctx)
  284. if self.clamp:
  285. if self.min is not None and rv < self.min:
  286. return self.min
  287. if self.max is not None and rv > self.max:
  288. return self.max
  289. if (
  290. self.min is not None
  291. and rv < self.min
  292. or self.max is not None
  293. and rv > self.max
  294. ):
  295. if self.min is None:
  296. self.fail(
  297. "{} is bigger than the maximum valid value {}.".format(
  298. rv, self.max
  299. ),
  300. param,
  301. ctx,
  302. )
  303. elif self.max is None:
  304. self.fail(
  305. "{} is smaller than the minimum valid value {}.".format(
  306. rv, self.min
  307. ),
  308. param,
  309. ctx,
  310. )
  311. else:
  312. self.fail(
  313. "{} is not in the valid range of {} to {}.".format(
  314. rv, self.min, self.max
  315. ),
  316. param,
  317. ctx,
  318. )
  319. return rv
  320. def __repr__(self):
  321. return "FloatRange({}, {})".format(self.min, self.max)
  322. class BoolParamType(ParamType):
  323. name = "boolean"
  324. def convert(self, value, param, ctx):
  325. if isinstance(value, bool):
  326. return bool(value)
  327. value = value.lower()
  328. if value in ("true", "t", "1", "yes", "y"):
  329. return True
  330. elif value in ("false", "f", "0", "no", "n"):
  331. return False
  332. self.fail("{} is not a valid boolean".format(value), param, ctx)
  333. def __repr__(self):
  334. return "BOOL"
  335. class UUIDParameterType(ParamType):
  336. name = "uuid"
  337. def convert(self, value, param, ctx):
  338. import uuid
  339. try:
  340. if PY2 and isinstance(value, text_type):
  341. value = value.encode("ascii")
  342. return uuid.UUID(value)
  343. except ValueError:
  344. self.fail("{} is not a valid UUID value".format(value), param, ctx)
  345. def __repr__(self):
  346. return "UUID"
  347. class File(ParamType):
  348. """Declares a parameter to be a file for reading or writing. The file
  349. is automatically closed once the context tears down (after the command
  350. finished working).
  351. Files can be opened for reading or writing. The special value ``-``
  352. indicates stdin or stdout depending on the mode.
  353. By default, the file is opened for reading text data, but it can also be
  354. opened in binary mode or for writing. The encoding parameter can be used
  355. to force a specific encoding.
  356. The `lazy` flag controls if the file should be opened immediately or upon
  357. first IO. The default is to be non-lazy for standard input and output
  358. streams as well as files opened for reading, `lazy` otherwise. When opening a
  359. file lazily for reading, it is still opened temporarily for validation, but
  360. will not be held open until first IO. lazy is mainly useful when opening
  361. for writing to avoid creating the file until it is needed.
  362. Starting with Click 2.0, files can also be opened atomically in which
  363. case all writes go into a separate file in the same folder and upon
  364. completion the file will be moved over to the original location. This
  365. is useful if a file regularly read by other users is modified.
  366. See :ref:`file-args` for more information.
  367. """
  368. name = "filename"
  369. envvar_list_splitter = os.path.pathsep
  370. def __init__(
  371. self, mode="r", encoding=None, errors="strict", lazy=None, atomic=False
  372. ):
  373. self.mode = mode
  374. self.encoding = encoding
  375. self.errors = errors
  376. self.lazy = lazy
  377. self.atomic = atomic
  378. def resolve_lazy_flag(self, value):
  379. if self.lazy is not None:
  380. return self.lazy
  381. if value == "-":
  382. return False
  383. elif "w" in self.mode:
  384. return True
  385. return False
  386. def convert(self, value, param, ctx):
  387. try:
  388. if hasattr(value, "read") or hasattr(value, "write"):
  389. return value
  390. lazy = self.resolve_lazy_flag(value)
  391. if lazy:
  392. f = LazyFile(
  393. value, self.mode, self.encoding, self.errors, atomic=self.atomic
  394. )
  395. if ctx is not None:
  396. ctx.call_on_close(f.close_intelligently)
  397. return f
  398. f, should_close = open_stream(
  399. value, self.mode, self.encoding, self.errors, atomic=self.atomic
  400. )
  401. # If a context is provided, we automatically close the file
  402. # at the end of the context execution (or flush out). If a
  403. # context does not exist, it's the caller's responsibility to
  404. # properly close the file. This for instance happens when the
  405. # type is used with prompts.
  406. if ctx is not None:
  407. if should_close:
  408. ctx.call_on_close(safecall(f.close))
  409. else:
  410. ctx.call_on_close(safecall(f.flush))
  411. return f
  412. except (IOError, OSError) as e: # noqa: B014
  413. self.fail(
  414. "Could not open file: {}: {}".format(
  415. filename_to_ui(value), get_streerror(e)
  416. ),
  417. param,
  418. ctx,
  419. )
  420. class Path(ParamType):
  421. """The path type is similar to the :class:`File` type but it performs
  422. different checks. First of all, instead of returning an open file
  423. handle it returns just the filename. Secondly, it can perform various
  424. basic checks about what the file or directory should be.
  425. .. versionchanged:: 6.0
  426. `allow_dash` was added.
  427. :param exists: if set to true, the file or directory needs to exist for
  428. this value to be valid. If this is not required and a
  429. file does indeed not exist, then all further checks are
  430. silently skipped.
  431. :param file_okay: controls if a file is a possible value.
  432. :param dir_okay: controls if a directory is a possible value.
  433. :param writable: if true, a writable check is performed.
  434. :param readable: if true, a readable check is performed.
  435. :param resolve_path: if this is true, then the path is fully resolved
  436. before the value is passed onwards. This means
  437. that it's absolute and symlinks are resolved. It
  438. will not expand a tilde-prefix, as this is
  439. supposed to be done by the shell only.
  440. :param allow_dash: If this is set to `True`, a single dash to indicate
  441. standard streams is permitted.
  442. :param path_type: optionally a string type that should be used to
  443. represent the path. The default is `None` which
  444. means the return value will be either bytes or
  445. unicode depending on what makes most sense given the
  446. input data Click deals with.
  447. """
  448. envvar_list_splitter = os.path.pathsep
  449. def __init__(
  450. self,
  451. exists=False,
  452. file_okay=True,
  453. dir_okay=True,
  454. writable=False,
  455. readable=True,
  456. resolve_path=False,
  457. allow_dash=False,
  458. path_type=None,
  459. ):
  460. self.exists = exists
  461. self.file_okay = file_okay
  462. self.dir_okay = dir_okay
  463. self.writable = writable
  464. self.readable = readable
  465. self.resolve_path = resolve_path
  466. self.allow_dash = allow_dash
  467. self.type = path_type
  468. if self.file_okay and not self.dir_okay:
  469. self.name = "file"
  470. self.path_type = "File"
  471. elif self.dir_okay and not self.file_okay:
  472. self.name = "directory"
  473. self.path_type = "Directory"
  474. else:
  475. self.name = "path"
  476. self.path_type = "Path"
  477. def coerce_path_result(self, rv):
  478. if self.type is not None and not isinstance(rv, self.type):
  479. if self.type is text_type:
  480. rv = rv.decode(get_filesystem_encoding())
  481. else:
  482. rv = rv.encode(get_filesystem_encoding())
  483. return rv
  484. def convert(self, value, param, ctx):
  485. rv = value
  486. is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-")
  487. if not is_dash:
  488. if self.resolve_path:
  489. rv = os.path.realpath(rv)
  490. try:
  491. st = os.stat(rv)
  492. except OSError:
  493. if not self.exists:
  494. return self.coerce_path_result(rv)
  495. self.fail(
  496. "{} '{}' does not exist.".format(
  497. self.path_type, filename_to_ui(value)
  498. ),
  499. param,
  500. ctx,
  501. )
  502. if not self.file_okay and stat.S_ISREG(st.st_mode):
  503. self.fail(
  504. "{} '{}' is a file.".format(self.path_type, filename_to_ui(value)),
  505. param,
  506. ctx,
  507. )
  508. if not self.dir_okay and stat.S_ISDIR(st.st_mode):
  509. self.fail(
  510. "{} '{}' is a directory.".format(
  511. self.path_type, filename_to_ui(value)
  512. ),
  513. param,
  514. ctx,
  515. )
  516. if self.writable and not os.access(value, os.W_OK):
  517. self.fail(
  518. "{} '{}' is not writable.".format(
  519. self.path_type, filename_to_ui(value)
  520. ),
  521. param,
  522. ctx,
  523. )
  524. if self.readable and not os.access(value, os.R_OK):
  525. self.fail(
  526. "{} '{}' is not readable.".format(
  527. self.path_type, filename_to_ui(value)
  528. ),
  529. param,
  530. ctx,
  531. )
  532. return self.coerce_path_result(rv)
  533. class Tuple(CompositeParamType):
  534. """The default behavior of Click is to apply a type on a value directly.
  535. This works well in most cases, except for when `nargs` is set to a fixed
  536. count and different types should be used for different items. In this
  537. case the :class:`Tuple` type can be used. This type can only be used
  538. if `nargs` is set to a fixed number.
  539. For more information see :ref:`tuple-type`.
  540. This can be selected by using a Python tuple literal as a type.
  541. :param types: a list of types that should be used for the tuple items.
  542. """
  543. def __init__(self, types):
  544. self.types = [convert_type(ty) for ty in types]
  545. @property
  546. def name(self):
  547. return "<{}>".format(" ".join(ty.name for ty in self.types))
  548. @property
  549. def arity(self):
  550. return len(self.types)
  551. def convert(self, value, param, ctx):
  552. if len(value) != len(self.types):
  553. raise TypeError(
  554. "It would appear that nargs is set to conflict with the"
  555. " composite type arity."
  556. )
  557. return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value))
  558. def convert_type(ty, default=None):
  559. """Converts a callable or python type into the most appropriate
  560. param type.
  561. """
  562. guessed_type = False
  563. if ty is None and default is not None:
  564. if isinstance(default, tuple):
  565. ty = tuple(map(type, default))
  566. else:
  567. ty = type(default)
  568. guessed_type = True
  569. if isinstance(ty, tuple):
  570. return Tuple(ty)
  571. if isinstance(ty, ParamType):
  572. return ty
  573. if ty is text_type or ty is str or ty is None:
  574. return STRING
  575. if ty is int:
  576. return INT
  577. # Booleans are only okay if not guessed. This is done because for
  578. # flags the default value is actually a bit of a lie in that it
  579. # indicates which of the flags is the one we want. See get_default()
  580. # for more information.
  581. if ty is bool and not guessed_type:
  582. return BOOL
  583. if ty is float:
  584. return FLOAT
  585. if guessed_type:
  586. return STRING
  587. # Catch a common mistake
  588. if __debug__:
  589. try:
  590. if issubclass(ty, ParamType):
  591. raise AssertionError(
  592. "Attempted to use an uninstantiated parameter type ({}).".format(ty)
  593. )
  594. except TypeError:
  595. pass
  596. return FuncParamType(ty)
  597. #: A dummy parameter type that just does nothing. From a user's
  598. #: perspective this appears to just be the same as `STRING` but internally
  599. #: no string conversion takes place. This is necessary to achieve the
  600. #: same bytes/unicode behavior on Python 2/3 in situations where you want
  601. #: to not convert argument types. This is usually useful when working
  602. #: with file paths as they can appear in bytes and unicode.
  603. #:
  604. #: For path related uses the :class:`Path` type is a better choice but
  605. #: there are situations where an unprocessed type is useful which is why
  606. #: it is is provided.
  607. #:
  608. #: .. versionadded:: 4.0
  609. UNPROCESSED = UnprocessedParamType()
  610. #: A unicode string parameter type which is the implicit default. This
  611. #: can also be selected by using ``str`` as type.
  612. STRING = StringParamType()
  613. #: An integer parameter. This can also be selected by using ``int`` as
  614. #: type.
  615. INT = IntParamType()
  616. #: A floating point value parameter. This can also be selected by using
  617. #: ``float`` as type.
  618. FLOAT = FloatParamType()
  619. #: A boolean parameter. This is the default for boolean flags. This can
  620. #: also be selected by using ``bool`` as a type.
  621. BOOL = BoolParamType()
  622. #: A UUID parameter.
  623. UUID = UUIDParameterType()