fix_map.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. # Copyright 2007 Google, Inc. All Rights Reserved.
  2. # Licensed to PSF under a Contributor Agreement.
  3. """Fixer that changes map(F, ...) into list(map(F, ...)) unless there
  4. exists a 'from future_builtins import map' statement in the top-level
  5. namespace.
  6. As a special case, map(None, X) is changed into list(X). (This is
  7. necessary because the semantics are changed in this case -- the new
  8. map(None, X) is equivalent to [(x,) for x in X].)
  9. We avoid the transformation (except for the special case mentioned
  10. above) if the map() call is directly contained in iter(<>), list(<>),
  11. tuple(<>), sorted(<>), ...join(<>), or for V in <>:.
  12. NOTE: This is still not correct if the original code was depending on
  13. map(F, X, Y, ...) to go on until the longest argument is exhausted,
  14. substituting None for missing values -- like zip(), it now stops as
  15. soon as the shortest argument is exhausted.
  16. """
  17. # Local imports
  18. from ..pgen2 import token
  19. from .. import fixer_base
  20. from ..fixer_util import Name, ArgList, Call, ListComp, in_special_context
  21. from ..pygram import python_symbols as syms
  22. from ..pytree import Node
  23. class FixMap(fixer_base.ConditionalFix):
  24. BM_compatible = True
  25. PATTERN = """
  26. map_none=power<
  27. 'map'
  28. trailer< '(' arglist< 'None' ',' arg=any [','] > ')' >
  29. [extra_trailers=trailer*]
  30. >
  31. |
  32. map_lambda=power<
  33. 'map'
  34. trailer<
  35. '('
  36. arglist<
  37. lambdef< 'lambda'
  38. (fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any
  39. >
  40. ','
  41. it=any
  42. >
  43. ')'
  44. >
  45. [extra_trailers=trailer*]
  46. >
  47. |
  48. power<
  49. 'map' args=trailer< '(' [any] ')' >
  50. [extra_trailers=trailer*]
  51. >
  52. """
  53. skip_on = 'future_builtins.map'
  54. def transform(self, node, results):
  55. if self.should_skip(node):
  56. return
  57. trailers = []
  58. if 'extra_trailers' in results:
  59. for t in results['extra_trailers']:
  60. trailers.append(t.clone())
  61. if node.parent.type == syms.simple_stmt:
  62. self.warning(node, "You should use a for loop here")
  63. new = node.clone()
  64. new.prefix = ""
  65. new = Call(Name("list"), [new])
  66. elif "map_lambda" in results:
  67. new = ListComp(results["xp"].clone(),
  68. results["fp"].clone(),
  69. results["it"].clone())
  70. new = Node(syms.power, [new] + trailers, prefix="")
  71. else:
  72. if "map_none" in results:
  73. new = results["arg"].clone()
  74. new.prefix = ""
  75. else:
  76. if "args" in results:
  77. args = results["args"]
  78. if args.type == syms.trailer and \
  79. args.children[1].type == syms.arglist and \
  80. args.children[1].children[0].type == token.NAME and \
  81. args.children[1].children[0].value == "None":
  82. self.warning(node, "cannot convert map(None, ...) "
  83. "with multiple arguments because map() "
  84. "now truncates to the shortest sequence")
  85. return
  86. new = Node(syms.power, [Name("map"), args.clone()])
  87. new.prefix = ""
  88. if in_special_context(node):
  89. return None
  90. new = Node(syms.power, [Name("list"), ArgList([new])] + trailers)
  91. new.prefix = ""
  92. new.prefix = node.prefix
  93. return new