Repository: frictionlessdata/frictionless-py

Issue:
Reshaping steps pivot/recast/transpose don't work
# Overview

After changing the transform API it doesn't work because we need to infer a schema for a reshaped table manually now

Discussion:

Patch:
diff --git a/docs/guides/transform-guide.md b/docs/guides/transform-guide.md
index 3a215ba5..2886c82f 100644
--- a/docs/guides/transform-guide.md
+++ b/docs/guides/transform-guide.md
@@ -75,27 +75,28 @@ There are dozens of other available steps that will be covered below.
 
 Transforming a package is not much more difficult than a resource. Basically, a package is a set of resources so we will be transforming resources exactly the same way as we did above and we will be managing the resources list itself, adding or removing them:
 
-> NOTE: This example is about to be fixed in https://github.com/frictionlessdata/frictionless-py/issues/715.
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
 
-source = Package(resources=[Resource(name='main', path="data/transform.csv")])
+source = Package(resources=[Resource(name="main", path="data/transform.csv")])
 target = transform(
     source,
     steps=[
-        steps.resource_add(name='extra', path='data/transform.csv'),
-        steps.resource_transform(name='main', steps=[
-            steps.table_merge(resource='extra'),
-            steps.row_sort(field_names=['id'])
-        ]),
+        steps.resource_add(name="extra", path="data/transform.csv"),
+        steps.resource_transform(
+            name="main",
+            steps=[
+                steps.table_merge(resource="extra"),
+                steps.row_sort(field_names=["id"]),
+            ],
+        ),
         steps.resource_remove(name="extra"),
     ],
 )
 pprint(target.resource_names)
-pprint(target.get_resource('main').schema)
-pprint(target.get_resource('main').read_rows())
+pprint(target.get_resource("main").schema)
+pprint(target.get_resource("main").read_rows())
 ```
 ```
 ['main']
@@ -122,6 +123,9 @@ A pipeline is a metadata object having one of these types:
 For resource and package types it's basically the same functionality as we have seen above but written declaratively. So let's run the same resource transformation as we did in the `Tranforming Resource` section:
 
 ```python title="Python"
+from pprint import pprint
+from frictionless import Pipeline, transform
+
 pipeline = Pipeline(
     {
         "tasks": [
@@ -194,18 +198,23 @@ See [Transform Steps](transform-steps.md) for a list of available steps.
 
 Here is an example of a custom step written as a Python function:
 
-> NOTE: This example is about to be fixed in https://github.com/frictionlessdata/frictionless-py/issues/715.
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
 
 def step(resource):
-    with resource:
-        resource.schema.remove_field("id")
-        for row in resource.row_stream:
-            del row["id"]
-            yield row
+    current = resource.to_copy()
+
+    # Data
+    def data():
+        with current:
+            for list in current.list_stream:
+                yield list[1:]
+
+    # Meta
+    resource.data = data
+    resource.schema.remove_field("id")
+
 
 source = Resource("data/transform.csv")
 target = transform(source, steps=[step])
@@ -248,4 +257,4 @@ print(petl_table)
 +----+---------+------------+
 | 3  | spain   | 47         |
 +----+---------+------------+
-```
\ No newline at end of file
+```
diff --git a/docs/guides/transform-steps.md b/docs/guides/transform-steps.md
index 25bd8220..17716e52 100644
--- a/docs/guides/transform-steps.md
+++ b/docs/guides/transform-steps.md
@@ -87,7 +87,6 @@ pprint(target.get_resource('extra').read_rows())
 
 ### Remove Resource
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -107,7 +106,6 @@ pprint(target)
 
 ### Transform Resource
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -143,7 +141,6 @@ pprint(target.get_resource('main').read_rows())
 
 ### Update Resource
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -183,7 +180,6 @@ These steps are meant to be used on a table level of a resource. This includes v
 
 ### Aggregate Table
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -210,7 +206,6 @@ pprint(target.read_rows())
 
 ### Attach Tables
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -237,7 +232,6 @@ pprint(target.read_rows())
 
 ### Debug Table
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -358,7 +352,6 @@ pprint(target.read_rows())
 
 ### Melt Table
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -388,7 +381,6 @@ pprint(target.read_rows())
 
 ### Merge Tables
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -520,7 +512,6 @@ pprint(target.read_rows())
 
 ### Validate Table
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -645,7 +636,6 @@ pprint(target.read_rows())
 
 ### Move Field
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -721,7 +711,6 @@ pprint(target.read_rows())
 
 ### Unpack Field
 
-
 ```python title="Python"
 from pprint import pprint
 from frictionless import Package, Resource, transform, steps
@@ -1106,4 +1095,4 @@ pprint(target.read_rows())
 [Row([('id', 1), ('name', 'germany'), ('population', 100)]),
  Row([('id', 2), ('name', 'france'), ('population', 100)]),
  Row([('id', 3), ('name', 'spain'), ('population', 100)])]
-```
\ No newline at end of file
+```
diff --git a/frictionless/steps/cell.py b/frictionless/steps/cell.py
index 6d196381..a7cd8137 100644
--- a/frictionless/steps/cell.py
+++ b/frictionless/steps/cell.py
@@ -19,17 +19,18 @@ class cell_convert(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         field_name = self.get("fieldName")
         function = self.get("function")
         value = self.get("value")
         if not field_name:
             if not function:
                 function = lambda input: value
-            yield from resource.to_petl().convertall(function)
+            resource.data = view.convertall(function)
         elif function:
-            yield from resource.to_petl().convert(field_name, function)
+            resource.data = view.convert(field_name, function)
         else:
-            yield from resource.to_petl().update(field_name, value)
+            resource.data = view.update(field_name, value)
 
     # Metadata
 
@@ -56,20 +57,21 @@ class cell_fill(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         value = self.get("value")
         field_name = self.get("fieldName")
         direction = self.get("direction")
         if value:
-            yield from resource.to_petl().convert(field_name, {None: value})
+            resource.data = view.convert(field_name, {None: value})
         elif direction == "down":
             if field_name:
-                yield from resource.to_petl().filldown(field_name)
+                resource.data = view.filldown(field_name)
             else:
-                yield from resource.to_petl().filldown()
+                resource.data = view.filldown()
         elif direction == "right":
-            yield from resource.to_petl().fillright()
+            resource.data = view.fillright()
         elif direction == "left":
-            yield from resource.to_petl().fillleft()
+            resource.data = view.fillleft()
 
     # Metadata
 
@@ -95,12 +97,13 @@ class cell_format(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         field_name = self.get("fieldName")
         template = self.get("template")
         if not field_name:
-            yield from resource.to_petl().formatall(template)
+            resource.data = view.formatall(template)
         else:
-            yield from resource.to_petl().format(field_name, template)
+            resource.data = view.format(field_name, template)
 
     # Metadata
 
@@ -127,10 +130,11 @@ class cell_interpolate(Step):
     def transform_resource(self, resource):
         template = self.get("template")
         field_name = self.get("fieldName")
+        view = resource.to_petl()
         if not field_name:
-            yield from resource.to_petl().interpolateall(template)
+            resource.data = view.interpolateall(template)
         else:
-            yield from resource.to_petl().interpolate(field_name, template)
+            resource.data = view.interpolate(field_name, template)
 
     # Metadata
 
@@ -156,18 +160,19 @@ class cell_replace(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         pattern = self.get("pattern")
         replace = self.get("replace")
         field_name = self.get("fieldName")
         if not field_name:
-            yield from resource.to_petl().replaceall(pattern, replace)
+            resource.data = view.replaceall(pattern, replace)
         else:
             pattern = pattern
             function = petl.replace
             if pattern.startswith("<regex>"):
                 pattern = pattern.replace("<regex>", "")
                 function = petl.sub
-            yield from function(resource.to_petl(), field_name, pattern, replace)
+            resource.data = function(view, field_name, pattern, replace)
 
     # Metadata
 
@@ -191,9 +196,10 @@ class cell_set(Step):
         super().__init__(descriptor)
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         value = self.get("value")
         field_name = self.get("fieldName")
-        yield from resource.to_petl().update(field_name, value)
+        resource.data = view.update(field_name, value)
 
     # Metadata
 
diff --git a/frictionless/steps/field.py b/frictionless/steps/field.py
index 8481ca09..40ae751b 100644
--- a/frictionless/steps/field.py
+++ b/frictionless/steps/field.py
@@ -36,6 +36,7 @@ class field_add(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         name = self.get("name")
         value = self.get("value")
         formula = self.get("formula")
@@ -43,19 +44,19 @@ class field_add(Step):
         position = self.get("position")
         incremental = self.get("incremental")
         options = self.get("options")
-        index = position - 1 if position else None
         field = Field(name=name, **options)
+        index = position - 1 if position else None
         if index is None:
             resource.schema.add_field(field)
         else:
             resource.schema.fields.insert(index, field)
         if incremental:
-            yield from resource.to_petl().addrownumbers(field=name)
+            resource.data = view.addrownumbers(field=name)
         else:
             if formula:
                 function = lambda row: simpleeval.simple_eval(formula, names=row)
             value = value or function
-            yield from resource.to_petl().addfield(name, value=value, index=index)
+            resource.data = view.addfield(name, value=value, index=index)
 
     # Metadata
 
@@ -81,11 +82,12 @@ class field_filter(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         names = self.get("names")
         for name in resource.schema.field_names:
             if name not in names:
                 resource.schema.remove_field(name)
-        yield from resource.to_petl().cut(*names)
+        resource.data = view.cut(*names)
 
     # Metadata
 
@@ -109,11 +111,12 @@ class field_move(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         name = self.get("name")
         position = self.get("position")
         field = resource.schema.remove_field(name)
         resource.schema.fields.insert(position - 1, field)
-        yield from resource.to_petl().movefield(name, position - 1)
+        resource.data = view.movefield(name, position - 1)
 
     # Metadata
 
@@ -137,10 +140,11 @@ class field_remove(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         names = self.get("names")
         for name in names:
             resource.schema.remove_field(name)
-        yield from resource.to_petl().cutout(*names)
+        resource.data = view.cutout(*names)
 
     # Metadata
 
@@ -174,6 +178,7 @@ class field_split(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         name = self.get("name")
         to_names = self.get("toNames")
         pattern = self.get("pattern")
@@ -186,8 +191,8 @@ class field_split(Step):
         # NOTE: this condition needs to be improved
         if "(" in pattern:
             processor = petl.capture
-        yield from processor(
-            resource.to_petl(),
+        resource.data = processor(
+            view,
             name,
             pattern,
             to_names,
@@ -220,6 +225,7 @@ class field_unpack(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         name = self.get("name")
         to_names = self.get("toNames")
         preserve = self.get("preserve")
@@ -229,11 +235,11 @@ class field_unpack(Step):
         if not preserve:
             resource.schema.remove_field(name)
         if field.type == "object":
-            processor = resource.to_petl().unpackdict
-            yield from processor(name, to_names, includeoriginal=preserve)
+            processor = view.unpackdict
+            resource.data = processor(name, to_names, includeoriginal=preserve)
         else:
-            processor = resource.to_petl().unpack
-            yield from processor(name, to_names, include_original=preserve)
+            processor = view.unpack
+            resource.data = processor(name, to_names, include_original=preserve)
 
     # Metadata
 
@@ -271,6 +277,7 @@ class field_update(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         name = self.get("name")
         value = self.get("value")
         formula = self.get("formula")
@@ -282,9 +289,9 @@ class field_update(Step):
         if formula:
             function = lambda val, row: simpleeval.simple_eval(formula, names=row)
         if function:
-            yield from resource.to_petl().convert(name, function)
-        else:
-            yield from resource.to_petl().update(name, value)
+            resource.data = view.convert(name, function)
+        elif "value" in self:
+            resource.data = view.update(name, value)
 
     # Metadata
 
diff --git a/frictionless/steps/row.py b/frictionless/steps/row.py
index 1a6fa3c7..3bfaf551 100644
--- a/frictionless/steps/row.py
+++ b/frictionless/steps/row.py
@@ -19,13 +19,14 @@ class row_filter(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         formula = self.get("formula")
         function = self.get("function")
         if formula:
             # NOTE: review EvalWithCompoundTypes/sync with checks
             evalclass = simpleeval.EvalWithCompoundTypes
             function = lambda row: evalclass(names=row).eval(formula)
-        yield from resource.to_petl().select(function)
+        resource.data = view.select(function)
 
     # Metadata
 
@@ -51,14 +52,15 @@ class row_search(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         regex = self.get("regex")
         field_name = self.get("fieldName")
         negate = self.get("negate")
         search = petl.searchcomplement if negate else petl.search
         if field_name:
-            yield from search(resource.to_petl(), field_name, regex)
+            resource.data = search(view, field_name, regex)
         else:
-            yield from search(resource.to_petl(), regex)
+            resource.data = search(view, regex)
 
     # Metadata
 
@@ -96,17 +98,18 @@ class row_slice(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         start = self.get("start")
         stop = self.get("stop")
         step = self.get("step")
         head = self.get("head")
         tail = self.get("tail")
         if head:
-            yield from resource.to_petl().head(head)
+            resource.data = view.head(head)
         elif tail:
-            yield from resource.to_petl().tail(tail)
+            resource.data = view.tail(tail)
         else:
-            yield from resource.to_petl().rowslice(start, stop, step)
+            resource.data = view.rowslice(start, stop, step)
 
     # Metadata
 
@@ -134,9 +137,10 @@ class row_sort(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         field_names = self.get("fieldNames")
         reverse = self.get("reverse")
-        yield from resource.to_petl().sort(field_names, reverse=reverse)
+        resource.data = view.sort(field_names, reverse=reverse)
 
     # Metadata
 
@@ -161,9 +165,10 @@ class row_split(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         pattern = self.get("pattern")
         field_name = self.get("fieldName")
-        yield from resource.to_petl().splitdown(field_name, pattern)
+        resource.data = view.splitdown(field_name, pattern)
 
     # Metadata
 
@@ -189,16 +194,17 @@ class row_subset(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         subset = self.get("subset")
         field_name = self.get("fieldName")
         if subset == "conflicts":
-            yield from resource.to_petl().conflicts(field_name)
+            resource.data = view.conflicts(field_name)
         elif subset == "distinct":
-            yield from resource.to_petl().distinct(field_name)
+            resource.data = view.distinct(field_name)
         elif subset == "duplicates":
-            yield from resource.to_petl().duplicates(field_name)
+            resource.data = view.duplicates(field_name)
         elif subset == "unique":
-            yield from resource.to_petl().unique(field_name)
+            resource.data = view.unique(field_name)
 
     # Metadata
 
@@ -230,14 +236,15 @@ class row_ungroup(Step):
         super().__init__(descriptor)
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         selection = self.get("selection")
         group_name = self.get("groupName")
         value_name = self.get("valueName")
         function = getattr(petl, f"groupselect{selection}")
         if selection in ["first", "last"]:
-            yield from function(resource.to_petl(), group_name)
+            resource.data = function(view, group_name)
         else:
-            yield from function(resource.to_petl(), group_name, value_name)
+            resource.data = function(view, group_name, value_name)
 
     # Metadata
 
diff --git a/frictionless/steps/table.py b/frictionless/steps/table.py
index 92cd3500..49167e4d 100644
--- a/frictionless/steps/table.py
+++ b/frictionless/steps/table.py
@@ -26,6 +26,7 @@ class table_aggregate(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         group_name = self.get("groupName")
         aggregation = self.get("aggregation")
         field = resource.schema.get_field(group_name)
@@ -33,7 +34,7 @@ class table_aggregate(Step):
         resource.schema.add_field(field)
         for name in aggregation.keys():
             resource.schema.add_field(Field(name=name))
-        yield from resource.to_petl().aggregate(group_name, aggregation)
+        resource.data = view.aggregate(group_name, aggregation)
 
     # Metadata
 
@@ -62,11 +63,11 @@ class table_attach(Step):
         if isinstance(source, str):
             source = target.package.get_resource(source)
         source.infer()
-        for field in source.schema.fields:
-            target.schema.fields.append(field.to_copy())
         view1 = target.to_petl()
         view2 = source.to_petl()
-        yield from petl.annex(view1, view2)
+        for field in source.schema.fields:
+            target.schema.fields.append(field.to_copy())
+        resource.data = petl.annex(view1, view2)
 
     # Metadata
 
@@ -89,11 +90,18 @@ class table_debug(Step):
     # Transform
 
     def transform_resource(self, resource):
+        current = resource.to_copy()
         function = self.get("function")
-        with resource:
-            for row in resource.row_stream:
-                function(row)
-                yield row
+
+        # Data
+        def data():
+            with current:
+                for row in current.row_stream:
+                    function(row)
+                    yield row
+
+        # Meta
+        resource.data = data
 
     # Metadata
 
@@ -138,7 +146,7 @@ class table_diff(Step):
         # NOTE: we might raise an error for ignore/hash
         if use_hash and not ignore_order:
             function = petl.hashcomplement
-        yield from function(view1, view2)
+        resource.data = function(view1, view2)
 
     # Metadata
 
@@ -173,7 +181,7 @@ class table_intersect(Step):
         view1 = target.to_petl()
         view2 = source.to_petl()
         function = petl.hashintersection if use_hash else petl.intersection
-        yield from function(view1, view2)
+        resource.data = function(view1, view2)
 
     # Metadata
 
@@ -225,20 +233,20 @@ class table_join(Step):
                     target.schema.fields.append(field.to_copy())
         if mode == "inner":
             join = petl.hashjoin if use_hash else petl.join
-            yield from join(view1, view2, field_name)
+            resource.data = join(view1, view2, field_name)
         elif mode == "left":
             leftjoin = petl.hashleftjoin if use_hash else petl.leftjoin
-            yield from leftjoin(view1, view2, field_name)
+            resource.data = leftjoin(view1, view2, field_name)
         elif mode == "right":
             rightjoin = petl.hashrightjoin if use_hash else petl.rightjoin
-            yield from rightjoin(view1, view2, field_name)
+            resource.data = rightjoin(view1, view2, field_name)
         elif mode == "outer":
-            yield from petl.outerjoin(view1, view2, field_name)
+            resource.data = petl.outerjoin(view1, view2, field_name)
         elif mode == "cross":
-            yield from petl.crossjoin(view1, view2)
+            resource.data = petl.crossjoin(view1, view2)
         elif mode == "negate":
             antijoin = petl.hashantijoin if use_hash else petl.antijoin
-            yield from antijoin(view1, view2, field_name)
+            resource.data = antijoin(view1, view2, field_name)
 
     # Metadata
 
@@ -274,6 +282,7 @@ class table_melt(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         variables = self.get("variables")
         field_name = self.get("fieldName")
         to_field_names = self.get("toFieldNames")
@@ -282,7 +291,7 @@ class table_melt(Step):
         resource.schema.add_field(field)
         for name in to_field_names:
             resource.schema.add_field(Field(name=name))
-        yield from resource.to_petl().melt(
+        resource.data = view.melt(
             key=field_name,
             variables=variables,
             variablefield=to_field_names[0],
@@ -338,7 +347,7 @@ class table_merge(Step):
         if ignore_fields:
             for field in source.schema.fields[len(target.schema.fields) :]:
                 target.schema.add_field(field)
-            yield from petl.stack(view1, view2)
+            resource.data = petl.stack(view1, view2)
 
         # Default
         else:
@@ -351,9 +360,9 @@ class table_merge(Step):
                         target.schema.remove_field(field.name)
             if sort_by_field:
                 key = sort_by_field
-                yield from petl.mergesort(view1, view2, key=key, header=field_names)
+                resource.data = petl.mergesort(view1, view2, key=key, header=field_names)
             else:
-                yield from petl.cat(view1, view2, header=field_names)
+                resource.data = petl.cat(view1, view2, header=field_names)
 
     # Metadata
 
@@ -375,10 +384,17 @@ class table_normalize(Step):
     # Transform
 
     def transform_resource(self, resource):
-        with resource:
-            yield resource.header.to_list()
-            for row in resource.row_stream:
-                yield row.to_list()
+        current = resource.to_copy()
+
+        # Data
+        def data():
+            with current:
+                yield current.header.to_list()
+                for row in current.row_stream:
+                    yield row.to_list()
+
+        # Meta
+        resource.data = data
 
     # Metadata
 
@@ -399,8 +415,11 @@ class table_pivot(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         options = self.get("options")
-        yield from resource.to_petl().pivot(**options)
+        resource.pop("schema", None)
+        resource.data = view.pivot(**options)
+        resource.infer()
 
     # Metadata
 
@@ -416,8 +435,9 @@ class table_print(Step):
 
     # Transform
 
-    def transform_resource(self, source, target):
-        print(source.to_petl().look(vrepr=str, style="simple"))
+    def transform_resource(self, resource):
+        view = resource.to_petl()
+        print(view.look(vrepr=str, style="simple"))
 
     # Metadata
 
@@ -446,13 +466,16 @@ class table_recast(Step):
     # Transform
 
     def transform_resource(self, resource):
+        view = resource.to_petl()
         field_name = self.get("fieldName")
         from_field_names = self.get("fromFieldNames")
-        yield from resource.to_petl().recast(
+        resource.pop("schema", None)
+        resource.data = view.recast(
             key=field_name,
             variablefield=from_field_names[0],
             valuefield=from_field_names[1],
         )
+        resource.infer()
 
     # Metadata
 
@@ -472,7 +495,10 @@ class table_transpose(Step):
     # Transform
 
     def transform_resource(self, resource):
-        yield resource.to_petl().transpose()
+        view = resource.to_petl()
+        resource.pop("schema", None)
+        resource.data = view.transpose()
+        resource.infer()
 
     # Metadata
 
@@ -489,14 +515,21 @@ class table_validate(Step):
     # Transform
 
     def transform_resource(self, resource):
-        with resource:
-            if not resource.header.valid:
-                raise FrictionlessException(error=resource.header.errors[0])
-            yield resource.header
-            for row in resource.row_stream:
-                if not row.valid:
-                    raise FrictionlessException(error=row.errors[0])
-                yield row
+        current = resource.to_copy()
+
+        # Data
+        def data():
+            with current:
+                if not current.header.valid:
+                    raise FrictionlessException(error=current.header.errors[0])
+                yield current.header
+                for row in current.row_stream:
+                    if not row.valid:
+                        raise FrictionlessException(error=row.errors[0])
+                    yield row
+
+        # Meta
+        resource.data = data
 
     # Metadata
 
diff --git a/frictionless/transform/resource.py b/frictionless/transform/resource.py
index 8ed86964..ed051d9c 100644
--- a/frictionless/transform/resource.py
+++ b/frictionless/transform/resource.py
@@ -44,35 +44,31 @@ def transform_resource(source, *, steps, **options):
 
     # Run transforms
     for step in steps:
+        data = resource.data
 
         # Transform
         try:
-            temp = resource.to_copy()
-            data = step.transform_resource(resource)
-            if data:
-                try:
-                    next(data)
-                except StopIteration:
-                    pass
-                data = DataWithErrorHandling(temp, step=step)
+            step.transform_resource(resource)
         except Exception as exception:
             error = errors.StepError(note=f'"{get_name(step)}" raises "{exception}"')
             raise FrictionlessException(error) from exception
 
         # Postprocess
-        if data:
-            resource = resource.to_copy()
-            resource.data = data
+        if resource.data is not data:
+            resource.data = DataWithErrorHandling(resource.data, step=step)
+            # NOTE:
+            # We need rework resource.data or move to resource.__setattr__
+            # https://github.com/frictionlessdata/frictionless-py/issues/722
             resource.scheme = ""
             resource.format = "inline"
-            resource.pop("path", None)
-            resource.pop("hashing", None)
-            resource.pop("encoding", None)
-            resource.pop("innerpath", None)
-            resource.pop("compression", None)
-            resource.pop("control", None)
-            resource.pop("dialect", None)
-            resource.pop("layout", None)
+            dict.pop(resource, "path", None)
+            dict.pop(resource, "hashing", None)
+            dict.pop(resource, "encoding", None)
+            dict.pop(resource, "innerpath", None)
+            dict.pop(resource, "compression", None)
+            dict.pop(resource, "control", None)
+            dict.pop(resource, "dialect", None)
+            dict.pop(resource, "layout", None)
 
     return resource
 
@@ -80,19 +76,14 @@ def transform_resource(source, *, steps, **options):
 # Internal
 
 
-# NOTE:
-# We might consider extending to the sample size
-# Also, we can move here some inferring logic (see pivor/recast/transpose)
-
-
 class DataWithErrorHandling:
-    def __init__(self, resource, *, step):
-        self.resource = resource
+    def __init__(self, data, *, step):
+        self.data = data
         self.step = step
 
     def __iter__(self):
         try:
-            yield from self.step.transform_resource(self.resource.to_copy())
+            yield from self.data() if callable(self.data) else self.data
         except Exception as exception:
             if isinstance(exception, FrictionlessException):
                 if exception.error.code == "step-error":


Test patch:
diff --git a/tests/steps/test_cell.py b/tests/steps/test_cell.py
index 0ee61ebe..f172c2fe 100644
--- a/tests/steps/test_cell.py
+++ b/tests/steps/test_cell.py
@@ -6,16 +6,21 @@ from frictionless import Resource, transform, steps
 
 def test_step_cell_convert():
     source = Resource(path="data/transform.csv")
-    source.infer()
-    source.schema.get_field("id").type = "string"
-    source.schema.get_field("population").type = "string"
     target = transform(
         source,
         steps=[
+            steps.field_update(name="id", type="string"),
+            steps.field_update(name="population", type="string"),
             steps.cell_convert(value="n/a"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "string"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "string"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": "n/a", "name": "n/a", "population": "n/a"},
         {"id": "n/a", "name": "n/a", "population": "n/a"},
@@ -25,14 +30,19 @@ def test_step_cell_convert():
 
 def test_step_cell_convert_with_field_name():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_convert(value="n/a", field_name="name"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "n/a", "population": 83},
         {"id": 2, "name": "n/a", "population": 66},
@@ -45,7 +55,6 @@ def test_step_cell_convert_with_field_name():
 
 def test_step_cell_fill():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
@@ -53,7 +62,13 @@ def test_step_cell_fill():
             steps.cell_fill(field_name="name", value="FRANCE"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "FRANCE", "population": 66},
@@ -63,7 +78,6 @@ def test_step_cell_fill():
 
 def test_step_cell_fill_direction_down():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
@@ -71,7 +85,13 @@ def test_step_cell_fill_direction_down():
             steps.cell_fill(direction="down"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "germany", "population": 66},
@@ -81,17 +101,23 @@ def test_step_cell_fill_direction_down():
 
 def test_step_cell_fill_direction_right():
     source = Resource(path="data/transform.csv")
-    source.infer()
-    source.schema.get_field("id").type = "string"
-    source.schema.get_field("population").type = "string"
     target = transform(
         source,
         steps=[
+            steps.field_update(name="id", type="string"),
+            steps.field_update(name="population", type="string"),
             steps.cell_replace(pattern="france", replace=None),
             steps.cell_fill(direction="right"),
         ],
     )
-    assert target.schema == source.schema
+    print(target.read_rows())
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "string"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "string"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": "1", "name": "germany", "population": "83"},
         {"id": "2", "name": "2", "population": "66"},
@@ -101,17 +127,22 @@ def test_step_cell_fill_direction_right():
 
 def test_step_cell_fill_direction_left():
     source = Resource(path="data/transform.csv")
-    source.infer()
-    source.schema.get_field("id").type = "string"
-    source.schema.get_field("population").type = "string"
     target = transform(
         source,
         steps=[
+            steps.field_update(name="id", type="string"),
+            steps.field_update(name="population", type="string"),
             steps.cell_replace(pattern="france", replace=None),
             steps.cell_fill(direction="left"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "string"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "string"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": "1", "name": "germany", "population": "83"},
         {"id": "2", "name": "66", "population": "66"},
@@ -124,16 +155,21 @@ def test_step_cell_fill_direction_left():
 
 def test_step_cell_format():
     source = Resource(path="data/transform.csv")
-    source.infer()
-    source.schema.get_field("id").type = "string"
-    source.schema.get_field("population").type = "string"
     target = transform(
         source,
         steps=[
+            steps.field_update(name="id", type="string"),
+            steps.field_update(name="population", type="string"),
             steps.cell_format(template="Prefix: {0}"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "string"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "string"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": "Prefix: 1", "name": "Prefix: germany", "population": "Prefix: 83"},
         {"id": "Prefix: 2", "name": "Prefix: france", "population": "Prefix: 66"},
@@ -143,14 +179,19 @@ def test_step_cell_format():
 
 def test_step_cell_format_with_name():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_format(template="Prefix: {0}", field_name="name"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "Prefix: germany", "population": 83},
         {"id": 2, "name": "Prefix: france", "population": 66},
@@ -163,16 +204,21 @@ def test_step_cell_format_with_name():
 
 def test_step_cell_interpolate():
     source = Resource(path="data/transform.csv")
-    source.infer()
-    source.schema.get_field("id").type = "string"
-    source.schema.get_field("population").type = "string"
     target = transform(
         source,
         steps=[
+            steps.field_update(name="id", type="string"),
+            steps.field_update(name="population", type="string"),
             steps.cell_interpolate(template="Prefix: %s"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "string"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "string"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": "Prefix: 1", "name": "Prefix: germany", "population": "Prefix: 83"},
         {"id": "Prefix: 2", "name": "Prefix: france", "population": "Prefix: 66"},
@@ -182,14 +228,19 @@ def test_step_cell_interpolate():
 
 def test_step_cell_interpolate_with_name():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_interpolate(template="Prefix: %s", field_name="name"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "Prefix: germany", "population": 83},
         {"id": 2, "name": "Prefix: france", "population": 66},
@@ -202,14 +253,19 @@ def test_step_cell_interpolate_with_name():
 
 def test_step_cell_replace():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_replace(pattern="france", replace="FRANCE"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "FRANCE", "population": 66},
@@ -219,14 +275,19 @@ def test_step_cell_replace():
 
 def test_step_cell_replace_with_field_name():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_replace(pattern="france", replace="FRANCE", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -236,7 +297,6 @@ def test_step_cell_replace_with_field_name():
 
 def test_step_cell_replace_using_regex():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
@@ -245,7 +305,13 @@ def test_step_cell_replace_using_regex():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "center", "population": 83},
         {"id": 2, "name": "center", "population": 66},
@@ -258,14 +324,19 @@ def test_step_cell_replace_using_regex():
 
 def test_step_cell_set():
     source = Resource(path="data/transform.csv")
-    source.infer()
     target = transform(
         source,
         steps=[
             steps.cell_set(field_name="population", value=100),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 100},
         {"id": 2, "name": "france", "population": 100},
diff --git a/tests/steps/test_resource.py b/tests/steps/test_resource.py
index 64d884a8..eab7ce12 100644
--- a/tests/steps/test_resource.py
+++ b/tests/steps/test_resource.py
@@ -6,7 +6,6 @@ from frictionless import Package, transform, steps
 
 def test_step_resource_add():
     source = Package("data/package/datapackage.json")
-    source.infer()
     target = transform(
         source,
         steps=[
@@ -27,7 +26,6 @@ def test_step_resource_add():
 
 def test_step_resource_remove():
     source = Package("data/package/datapackage.json")
-    source.infer()
     target = transform(
         source,
         steps=[
@@ -80,7 +78,6 @@ def test_step_resource_transform():
 
 def test_step_resource_update():
     source = Package("data/package/datapackage.json")
-    source.infer()
     target = transform(
         source,
         steps=[
diff --git a/tests/steps/test_row.py b/tests/steps/test_row.py
index 3c51aa2b..07e1b1d2 100644
--- a/tests/steps/test_row.py
+++ b/tests/steps/test_row.py
@@ -5,8 +5,7 @@ from frictionless import Resource, transform, steps
 
 
 def test_step_row_filter():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -14,7 +13,13 @@ def test_step_row_filter():
             steps.row_filter(formula="id > 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -22,8 +27,7 @@ def test_step_row_filter():
 
 
 def test_step_row_filter_with_function():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -31,7 +35,13 @@ def test_step_row_filter_with_function():
             steps.row_filter(function=lambda row: row["id"] > 1),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -39,8 +49,7 @@ def test_step_row_filter_with_function():
 
 
 def test_step_row_filter_petl_selectop():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -48,15 +57,20 @@ def test_step_row_filter_petl_selectop():
             steps.row_filter(formula="id == 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selecteq():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -64,15 +78,20 @@ def test_step_row_filter_petl_selecteq():
             steps.row_filter(formula="id == 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectne():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -80,7 +99,13 @@ def test_step_row_filter_petl_selectne():
             steps.row_filter(formula="id != 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -88,8 +113,7 @@ def test_step_row_filter_petl_selectne():
 
 
 def test_step_row_filter_petl_selectlt():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -97,15 +121,20 @@ def test_step_row_filter_petl_selectlt():
             steps.row_filter(formula="id < 2"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectle():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -113,7 +142,13 @@ def test_step_row_filter_petl_selectle():
             steps.row_filter(formula="id <= 2"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -121,8 +156,7 @@ def test_step_row_filter_petl_selectle():
 
 
 def test_step_row_filter_petl_selectgt():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -130,15 +164,20 @@ def test_step_row_filter_petl_selectgt():
             steps.row_filter(formula="id > 2"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 3, "name": "spain", "population": 47},
     ]
 
 
 def test_step_row_filter_petl_selectge():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -146,7 +185,13 @@ def test_step_row_filter_petl_selectge():
             steps.row_filter(formula="id >= 2"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -154,8 +199,7 @@ def test_step_row_filter_petl_selectge():
 
 
 def test_step_row_filter_petl_selectrangeopen():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -163,7 +207,13 @@ def test_step_row_filter_petl_selectrangeopen():
             steps.row_filter(formula="1 <= id <= 3"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -172,8 +222,7 @@ def test_step_row_filter_petl_selectrangeopen():
 
 
 def test_step_row_filter_petl_selectrangeopenleft():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -181,7 +230,13 @@ def test_step_row_filter_petl_selectrangeopenleft():
             steps.row_filter(formula="1 <= id < 3"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -189,8 +244,7 @@ def test_step_row_filter_petl_selectrangeopenleft():
 
 
 def test_step_row_filter_petl_selectrangeopenright():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -198,7 +252,13 @@ def test_step_row_filter_petl_selectrangeopenright():
             steps.row_filter(formula="1 < id <= 3"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -206,8 +266,7 @@ def test_step_row_filter_petl_selectrangeopenright():
 
 
 def test_step_row_filter_petl_selectrangeclosed():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -215,30 +274,40 @@ def test_step_row_filter_petl_selectrangeclosed():
             steps.row_filter(formula="1 < id < 3"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_row_filter_petl_selectcontains():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(formula="'er' in name"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectin():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -246,15 +315,20 @@ def test_step_row_filter_petl_selectin():
             steps.row_filter(formula="id in [1]"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectnoin():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -262,15 +336,20 @@ def test_step_row_filter_petl_selectnoin():
             steps.row_filter(formula="id not in [2, 3]"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectis():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -278,15 +357,20 @@ def test_step_row_filter_petl_selectis():
             steps.row_filter(formula="id is 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_filter_petl_selectisnot():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -294,7 +378,13 @@ def test_step_row_filter_petl_selectisnot():
             steps.row_filter(formula="id is not 1"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -302,8 +392,7 @@ def test_step_row_filter_petl_selectisnot():
 
 
 def test_step_row_filter_petl_selectisinstance():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -311,7 +400,13 @@ def test_step_row_filter_petl_selectisinstance():
             steps.row_filter(function=lambda row: isinstance(row["id"], int)),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -320,15 +415,20 @@ def test_step_row_filter_petl_selectisinstance():
 
 
 def test_step_row_filter_petl_selectistrue():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(function=lambda row: bool(row["id"])),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -337,41 +437,56 @@ def test_step_row_filter_petl_selectistrue():
 
 
 def test_step_row_filter_petl_selectisfalse():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(function=lambda row: not bool(row["id"])),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == []
 
 
 def test_step_row_filter_petl_selectnone():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(formula="id is None"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == []
 
 
 def test_step_row_filter_petl_selectisnone():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(formula="id is not None"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -380,15 +495,20 @@ def test_step_row_filter_petl_selectisnone():
 
 
 def test_step_row_filter_petl_rowlenselect():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_filter(function=lambda row: len(row) == 3),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -400,45 +520,60 @@ def test_step_row_filter_petl_rowlenselect():
 
 
 def test_step_row_search():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_search(regex=r"^f.*"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_row_search_with_name():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_search(regex=r"^f.*", field_name="name"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_row_search_with_negate():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_search(regex=r"^f.*", negate=True),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 3, "name": "spain", "population": 47},
@@ -449,15 +584,20 @@ def test_step_row_search_with_negate():
 
 
 def test_step_row_slice():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_slice(stop=2),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -465,45 +605,60 @@ def test_step_row_slice():
 
 
 def test_step_row_slice_with_start():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_slice(start=1, stop=2),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_row_slice_with_start_and_step():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_slice(start=1, stop=3, step=2),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_row_slice_with_head():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_slice(head=2),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -511,15 +666,20 @@ def test_step_row_slice_with_head():
 
 
 def test_step_row_slice_with_tail():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_slice(tail=2),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 3, "name": "spain", "population": 47},
@@ -530,15 +690,20 @@ def test_step_row_slice_with_tail():
 
 
 def test_step_row_sort():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_sort(field_names=["name"]),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
         {"id": 1, "name": "germany", "population": 83},
@@ -547,15 +712,20 @@ def test_step_row_sort():
 
 
 def test_step_row_sort_with_reverse():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_sort(field_names=["id"], reverse=True),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 3, "name": "spain", "population": 47},
         {"id": 2, "name": "france", "population": 66},
@@ -567,15 +737,20 @@ def test_step_row_sort_with_reverse():
 
 
 def test_step_row_split():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_split(field_name="name", pattern="a"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germ", "population": 83},
         {"id": 1, "name": "ny", "population": 83},
@@ -590,21 +765,25 @@ def test_step_row_split():
 
 
 def test_step_row_subset_conflicts():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_subset(subset="conflicts", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == []
 
 
 def test_step_row_subset_conflicts_with_duplicates():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -612,7 +791,13 @@ def test_step_row_subset_conflicts_with_duplicates():
             steps.row_subset(subset="conflicts", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 1, "name": "france", "population": 66},
@@ -621,15 +806,20 @@ def test_step_row_subset_conflicts_with_duplicates():
 
 
 def test_step_row_subset_distinct():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_subset(subset="distinct", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -638,8 +828,7 @@ def test_step_row_subset_distinct():
 
 
 def test_step_row_subset_distinct_with_duplicates():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -647,28 +836,38 @@ def test_step_row_subset_distinct_with_duplicates():
             steps.row_subset(subset="distinct", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
     ]
 
 
 def test_step_row_subset_duplicates():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_subset(subset="duplicates"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == []
 
 
 def test_step_row_subset_duplicates_with_name():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -676,7 +875,13 @@ def test_step_row_subset_duplicates_with_name():
             steps.row_subset(subset="duplicates", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 1, "name": "france", "population": 66},
@@ -685,15 +890,20 @@ def test_step_row_subset_duplicates_with_name():
 
 
 def test_step_row_subset_unique():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
             steps.row_subset(subset="unique"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -702,8 +912,7 @@ def test_step_row_subset_unique():
 
 
 def test_step_row_subset_unique_with_name():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -711,7 +920,13 @@ def test_step_row_subset_unique_with_name():
             steps.row_subset(subset="unique", field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == []
 
 
@@ -719,15 +934,21 @@ def test_step_row_subset_unique_with_name():
 
 
 def test_step_row_ungroup_first():
-    source = Resource(path="data/transform-groups.csv")
-    source.infer()
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
             steps.row_ungroup(group_name="name", selection="first"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+            {"name": "year", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 3, "name": "france", "population": 66, "year": 2020},
         {"id": 1, "name": "germany", "population": 83, "year": 2020},
@@ -736,15 +957,21 @@ def test_step_row_ungroup_first():
 
 
 def test_step_row_ungroup_last():
-    source = Resource(path="data/transform-groups.csv")
-    source.infer()
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
             steps.row_ungroup(group_name="name", selection="last"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+            {"name": "year", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 4, "name": "france", "population": 54, "year": 1920},
         {"id": 2, "name": "germany", "population": 77, "year": 1920},
@@ -753,8 +980,7 @@ def test_step_row_ungroup_last():
 
 
 def test_step_row_ungroup_min():
-    source = Resource(path="data/transform-groups.csv")
-    source.infer()
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
@@ -763,7 +989,14 @@ def test_step_row_ungroup_min():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+            {"name": "year", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 4, "name": "france", "population": 54, "year": 1920},
         {"id": 2, "name": "germany", "population": 77, "year": 1920},
@@ -772,8 +1005,7 @@ def test_step_row_ungroup_min():
 
 
 def test_step_row_ungroup_max():
-    source = Resource(path="data/transform-groups.csv")
-    source.infer()
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
@@ -782,7 +1014,14 @@ def test_step_row_ungroup_max():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+            {"name": "year", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 3, "name": "france", "population": 66, "year": 2020},
         {"id": 1, "name": "germany", "population": 83, "year": 2020},
diff --git a/tests/steps/test_table.py b/tests/steps/test_table.py
index 46a11f95..e4c3ab3c 100644
--- a/tests/steps/test_table.py
+++ b/tests/steps/test_table.py
@@ -6,7 +6,7 @@ from frictionless import Resource, FrictionlessException, transform, steps
 
 
 def test_step_table_aggregate():
-    source = Resource(path="data/transform-groups.csv")
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
@@ -30,7 +30,7 @@ def test_step_table_aggregate():
 
 
 def test_step_table_aggregate_multiple():
-    source = Resource(path="data/transform-groups.csv")
+    source = Resource("data/transform-groups.csv")
     target = transform(
         source,
         steps=[
@@ -64,7 +64,7 @@ def test_step_table_aggregate_multiple():
 
 
 def test_step_table_attach():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -90,8 +90,7 @@ def test_step_table_attach():
 
 
 def test_step_table_diff():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -108,15 +107,20 @@ def test_step_table_diff():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_table_diff_with_ignore_order():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -133,15 +137,20 @@ def test_step_table_diff_with_ignore_order():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
 
 
 def test_step_table_diff_with_use_hash():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -159,7 +168,13 @@ def test_step_table_diff_with_use_hash():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 2, "name": "france", "population": 66},
     ]
@@ -169,8 +184,7 @@ def test_step_table_diff_with_use_hash():
 
 
 def test_step_table_intersect():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -187,7 +201,13 @@ def test_step_table_intersect():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 3, "name": "spain", "population": 47},
@@ -195,8 +215,7 @@ def test_step_table_intersect():
 
 
 def test_step_table_intersect_with_use_hash():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -214,7 +233,13 @@ def test_step_table_intersect_with_use_hash():
             ),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 3, "name": "spain", "population": 47},
@@ -225,7 +250,7 @@ def test_step_table_intersect_with_use_hash():
 
 
 def test_step_table_join():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -251,7 +276,7 @@ def test_step_table_join():
 
 
 def test_step_table_join_with_name_is_not_first_field():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -278,7 +303,7 @@ def test_step_table_join_with_name_is_not_first_field():
 
 
 def test_step_table_join_mode_left():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -306,7 +331,7 @@ def test_step_table_join_mode_left():
 
 
 def test_step_table_join_mode_right():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -333,7 +358,7 @@ def test_step_table_join_mode_right():
 
 
 def test_step_table_join_mode_outer():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -362,7 +387,7 @@ def test_step_table_join_mode_outer():
 
 
 def test_step_table_join_mode_cross():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -392,7 +417,7 @@ def test_step_table_join_mode_cross():
 
 
 def test_step_table_join_mode_negate():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -416,7 +441,7 @@ def test_step_table_join_mode_negate():
 
 
 def test_step_table_join_hash_is_true():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -446,7 +471,7 @@ def test_step_table_join_hash_is_true():
 
 
 def test_step_table_melt():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -472,7 +497,7 @@ def test_step_table_melt():
 
 
 def test_step_table_melt_with_variables():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -495,7 +520,7 @@ def test_step_table_melt_with_variables():
 
 
 def test_step_table_melt_with_to_field_names():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -523,7 +548,7 @@ def test_step_table_melt_with_to_field_names():
 
 
 def test_step_table_merge():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -549,7 +574,7 @@ def test_step_table_merge():
 
 
 def test_step_table_merge_with_field_names():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -574,7 +599,7 @@ def test_step_table_merge_with_field_names():
 
 
 def test_step_merge_ignore_fields():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -600,7 +625,7 @@ def test_step_merge_ignore_fields():
 
 
 def test_step_table_merge_with_sort():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -628,9 +653,8 @@ def test_step_table_merge_with_sort():
 # Pivot
 
 
-@pytest.mark.xfail(reason="steps.pivot/recast/transpose")
 def test_step_table_pivot():
-    source = Resource(path="data/transform-pivot.csv")
+    source = Resource("data/transform-pivot.csv")
     target = transform(
         source,
         steps=[
@@ -638,6 +662,7 @@ def test_step_table_pivot():
             steps.table_pivot(f1="region", f2="gender", f3="units", aggfun=sum),
         ],
     )
+    print(target.schema)
     assert target.schema == {
         "fields": [
             {"name": "region", "type": "string"},
@@ -654,12 +679,8 @@ def test_step_table_pivot():
 # Recast
 
 
-# We need to infer schema in the processor
-# We might merge this logic with transform.DataWithErrorHandling
-@pytest.mark.xfail(reason="steps.pivot/recast/transpose")
 def test_step_table_recast():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -668,7 +689,14 @@ def test_step_table_recast():
             steps.table_recast(field_name="id"),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
+
     assert target.read_rows() == [
         {"id": 1, "name": "germany", "population": 83},
         {"id": 2, "name": "france", "population": 66},
@@ -679,9 +707,8 @@ def test_step_table_recast():
 # Transpose
 
 
-@pytest.mark.xfail(reason="steps.pivot/recast/transpose")
 def test_step_table_transpose():
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -706,8 +733,7 @@ def test_step_table_transpose():
 
 
 def test_step_table_validate():
-    source = Resource(path="data/transform.csv")
-    source.infer()
+    source = Resource("data/transform.csv")
     target = transform(
         source,
         steps=[
@@ -715,7 +741,13 @@ def test_step_table_validate():
             steps.table_validate(),
         ],
     )
-    assert target.schema == source.schema
+    assert target.schema == {
+        "fields": [
+            {"name": "id", "type": "integer"},
+            {"name": "name", "type": "string"},
+            {"name": "population", "type": "integer"},
+        ]
+    }
     with pytest.raises(FrictionlessException) as excinfo:
         target.read_rows()
     error = excinfo.value.error
@@ -730,7 +762,7 @@ def test_step_table_write(tmpdir):
     path = str(tmpdir.join("table.json"))
 
     # Write
-    source = Resource(path="data/transform.csv")
+    source = Resource("data/transform.csv")
     transform(
         source,
         steps=[
diff --git a/tests/transform/test_main.py b/tests/transform/test_main.py
index df85babc..6cff5bf3 100644
--- a/tests/transform/test_main.py
+++ b/tests/transform/test_main.py
@@ -35,10 +35,17 @@ def test_transform_custom_step_function_based():
 
     # Create step
     def custom(resource):
-        with resource:
-            for row in resource.row_stream:
-                row["id"] = row["id"] * row["id"]
-                yield row
+        current = resource.to_copy()
+
+        # Data
+        def data():
+            with current:
+                for row in current.row_stream:
+                    row["id"] = row["id"] * row["id"]
+                    yield row
+
+        # Meta
+        resource.data = data
 
     # Transform resource
     source = Resource(path="data/transform.csv")
diff --git a/tests/transform/test_package.py b/tests/transform/test_package.py
index 1ce149e3..930226db 100644
--- a/tests/transform/test_package.py
+++ b/tests/transform/test_package.py
@@ -1,11 +1,9 @@
-import pytest
 from frictionless import transform, describe, steps
 
 
 # General
 
 
-@pytest.mark.xfail(reason="steps.pivot/recast/transpose")
 def test_transform_package():
     source = describe("data/tables/chunk*.csv")
     target = transform(