From 078faf1591211528207de49e05f2be4ec1b5ee91 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 11:25:08 -0700 Subject: [PATCH 01/12] run black on code to insure consistency + pep8 compliance --- tests/test_cli_create.py | 113 ++++++++++---- tests/test_cli_jobs.py | 42 +++-- tests/test_cli_list.py | 32 ++-- tests/test_cli_publish.py | 29 ++-- tests/test_cli_sources.py | 57 ++++--- tests/test_cli_status.py | 19 ++- tests/test_cli_update_recipe.py | 28 ++-- tests/test_cli_validate_recipe.py | 23 +-- tests/test_cli_view_recipe.py | 15 +- tests/test_file_validator.py | 19 ++- tilesets/__init__.py | 2 +- tilesets/cli.py | 250 +++++++++++++++++++----------- tilesets/scripts/utils.py | 103 ++++++------ 13 files changed, 456 insertions(+), 276 deletions(-) diff --git a/tests/test_cli_create.py b/tests/test_cli_create.py index 1a7f660..1067249 100644 --- a/tests/test_cli_create.py +++ b/tests/test_cli_create.py @@ -7,9 +7,10 @@ from tilesets.cli import create -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text + def MockResponse(self): return self @@ -18,7 +19,7 @@ def MockResponse(self): def test_cli_create_missing_recipe(): runner = CliRunner() # missing --recipe option - result = runner.invoke(create, ['test.id']) + result = runner.invoke(create, ["test.id"]) assert result.exit_code == 2 assert 'Missing option "--recipe"' in result.output @@ -27,69 +28,119 @@ def test_cli_create_missing_recipe(): def test_cli_create_missing_name(): runner = CliRunner() # missing --name option - result = runner.invoke(create, ['test.id', '--recipe', 'tests/fixtures/recipe.json']) + result = runner.invoke( + create, ["test.id", "--recipe", "tests/fixtures/recipe.json"] + ) assert result.exit_code == 2 assert 'Missing option "--name"' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_create_success(mock_request_post): runner = CliRunner() # sends request to proper endpoints mock_request_post.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(create, ['test.id', '--recipe', 'tests/fixtures/recipe.json', '--name', 'test name']) + result = runner.invoke( + create, + ["test.id", "--recipe", "tests/fixtures/recipe.json", "--name", "test name"], + ) assert result.exit_code == 0 - mock_request_post.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id?access_token=fake-token', json={'name': 'test name', 'description': '', 'recipe': {'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'}}) + mock_request_post.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id?access_token=fake-token", + json={ + "name": "test name", + "description": "", + "recipe": {"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, + }, + ) assert '{\n "message": "mock message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_create_success_description(mock_request_post): runner = CliRunner() # sends request with "description" included - mock_request_post.return_value = MockResponse('{"message":"mock message with description"}') - result = runner.invoke(create, [ - 'test.id', - '--recipe', - 'tests/fixtures/recipe.json', - '--name', - 'test name', - '--description', - 'test description'] + mock_request_post.return_value = MockResponse( + '{"message":"mock message with description"}' + ) + result = runner.invoke( + create, + [ + "test.id", + "--recipe", + "tests/fixtures/recipe.json", + "--name", + "test name", + "--description", + "test description", + ], ) assert result.exit_code == 0 - mock_request_post.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id?access_token=fake-token', json={'name': 'test name', 'description': 'test description', 'recipe': {'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'}}) + mock_request_post.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id?access_token=fake-token", + json={ + "name": "test name", + "description": "test description", + "recipe": {"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, + }, + ) assert '{\n "message": "mock message with description"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_create_private_invalid(mock_request_post): runner = CliRunner() # sends request with "description" included - mock_request_post.return_value = MockResponse('{"message":"mock message with description"}') - result = runner.invoke(create, [ - 'test.id', - '--recipe', - 'tests/fixtures/recipe.json', - '--name', - 'test name', - '--privacy', - 'invalid-privacy-value'] + mock_request_post.return_value = MockResponse( + '{"message":"mock message with description"}' + ) + result = runner.invoke( + create, + [ + "test.id", + "--recipe", + "tests/fixtures/recipe.json", + "--name", + "test name", + "--privacy", + "invalid-privacy-value", + ], ) assert result.exit_code == 2 - assert 'Invalid value for "--privacy" / "-p": invalid choice: invalid-privacy-value. (choose from public, private)' in result.output + assert ( + 'Invalid value for "--privacy" / "-p": invalid choice: invalid-privacy-value. (choose from public, private)' + in result.output + ) @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_use_token_flag(mock_request_post): runner = CliRunner() mock_request_post.return_value = MockResponse('{"message":"mock message"}') # Provides the flag --token - result = runner.invoke(create, ['test.id', '--recipe', 'tests/fixtures/recipe.json', '--name', 'test name', '--token', 'flag-token']) + result = runner.invoke( + create, + [ + "test.id", + "--recipe", + "tests/fixtures/recipe.json", + "--name", + "test name", + "--token", + "flag-token", + ], + ) assert result.exit_code == 0 - mock_request_post.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id?access_token=flag-token', json={'name': 'test name', 'description': '', 'recipe': {'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'}}) + mock_request_post.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id?access_token=flag-token", + json={ + "name": "test name", + "description": "", + "recipe": {"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, + }, + ) assert '{\n "message": "mock message"\n}\n' in result.output diff --git a/tests/test_cli_jobs.py b/tests/test_cli_jobs.py index a37f7aa..fa8823e 100644 --- a/tests/test_cli_jobs.py +++ b/tests/test_cli_jobs.py @@ -6,71 +6,83 @@ from tilesets.cli import jobs, job -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text self.status_code = 200 + def MockResponse(self): return self -class MockResponseError(): +class MockResponseError: def __init__(self, mock_text): self.text = mock_text self.status_code = 404 + def MockResponse(self): return self @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_job(mock_request_get): runner = CliRunner() # sends expected request mock_request_get.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(jobs, ['test.id']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/jobs?access_token=fake-token') + result = runner.invoke(jobs, ["test.id"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/jobs?access_token=fake-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_job_error(mock_request_get): runner = CliRunner() # sends expected request - mock_request_get.return_value = MockResponseError('{"message":"mock error message"}') - result = runner.invoke(jobs, ['test.id']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/jobs?access_token=fake-token') + mock_request_get.return_value = MockResponseError( + '{"message":"mock error message"}' + ) + result = runner.invoke(jobs, ["test.id"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/jobs?access_token=fake-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock error message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_jobs_and_stage(mock_request_get): """test jobs + stage endpoint""" runner = CliRunner() # sends expected request mock_request_get.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(jobs, ['test.id', '--stage', 'complete']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/jobs?stage=complete&access_token=fake-token') + result = runner.invoke(jobs, ["test.id", "--stage", "complete"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/jobs?stage=complete&access_token=fake-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_single_job(mock_request_get): """test job endpoint""" runner = CliRunner() # sends expected request mock_request_get.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(job, ['test.id', 'job_id']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/jobs/job_id?access_token=fake-token') + result = runner.invoke(job, ["test.id", "job_id"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/jobs/job_id?access_token=fake-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output diff --git a/tests/test_cli_list.py b/tests/test_cli_list.py index eef8838..ef1ec18 100644 --- a/tests/test_cli_list.py +++ b/tests/test_cli_list.py @@ -6,44 +6,54 @@ from tilesets.cli import list -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text self.status_code = 200 + def MockResponse(self): return self -class MockResponseError(): +class MockResponseError: def __init__(self, mock_text): self.text = mock_text self.status_code = 404 + def MockResponse(self): return self @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_list(mock_request_get): runner = CliRunner() # sends expected request - mock_request_get.return_value = MockResponse('[{"id":"test.tileset-1","something":"beep"},{"id":"test.tileset-2","something":"boop"}]'); - result = runner.invoke(list, ['test']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test?access_token=fake-token') + mock_request_get.return_value = MockResponse( + '[{"id":"test.tileset-1","something":"beep"},{"id":"test.tileset-2","something":"boop"}]' + ) + result = runner.invoke(list, ["test"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test?access_token=fake-token" + ) assert result.exit_code == 0 - assert 'test.tileset-1\ntest.tileset-2\n' in result.output + assert "test.tileset-1\ntest.tileset-2\n" in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_list_verbose(mock_request_get): runner = CliRunner() # sends expected request - mock_request_get.return_value = MockResponse('[{"id":"test.tileset-1","something":"beep"},{"id":"test.tileset-2","something":"boop"}]'); - result = runner.invoke(list, ['test', '--verbose']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test?access_token=fake-token') + mock_request_get.return_value = MockResponse( + '[{"id":"test.tileset-1","something":"beep"},{"id":"test.tileset-2","something":"boop"}]' + ) + result = runner.invoke(list, ["test", "--verbose"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test?access_token=fake-token" + ) assert result.exit_code == 0 assert '"something": "beep"\n' in result.output assert '"something": "boop"\n' in result.output diff --git a/tests/test_cli_publish.py b/tests/test_cli_publish.py index 1c9a8a4..992fff7 100644 --- a/tests/test_cli_publish.py +++ b/tests/test_cli_publish.py @@ -6,34 +6,45 @@ from tilesets.cli import publish -class MockResponse(): +class MockResponse: def __init__(self, mock_text, status_code): self.text = mock_text self.status_code = status_code + def MockResponse(self): return self @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_publish(mock_request_post): runner = CliRunner() # sends expected request mock_request_post.return_value = MockResponse('{"message":"mock message"}', 200) - result = runner.invoke(publish, ['test.id']) - mock_request_post.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/publish?access_token=fake-token') + result = runner.invoke(publish, ["test.id"]) + mock_request_post.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/publish?access_token=fake-token" + ) assert result.exit_code == 0 - assert 'You can view the status of your tileset with the `tilesets status test.id` command.' in result.output + assert ( + "You can view the status of your tileset with the `tilesets status test.id` command." + in result.output + ) @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_publish_use_token_flag(mock_request_post): runner = CliRunner() mock_request_post.return_value = MockResponse('{"message":"mock message"}', 200) # Provides the flag --token - result = runner.invoke(publish, ['test.id', '--token', 'flag-token']) - mock_request_post.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/publish?access_token=flag-token') + result = runner.invoke(publish, ["test.id", "--token", "flag-token"]) + mock_request_post.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/publish?access_token=flag-token" + ) assert result.exit_code == 0 - assert 'You can view the status of your tileset with the `tilesets status test.id` command.' in result.output + assert ( + "You can view the status of your tileset with the `tilesets status test.id` command." + in result.output + ) diff --git a/tests/test_cli_sources.py b/tests/test_cli_sources.py index 5365a0c..8dca2bd 100644 --- a/tests/test_cli_sources.py +++ b/tests/test_cli_sources.py @@ -9,62 +9,79 @@ view_source, delete_source, validate_source, - list_sources + list_sources, ) -class MockResponse(): +class MockResponse: def __init__(self, mock_text, status_code): self.text = mock_text self.status_code = status_code + def MockResponse(self): return self + def json(self): return json.loads(self.text) @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.post') +@mock.patch("requests.post") def test_cli_add_source(mock_request_post): - mock_request_post.return_value = MockResponse('{"id":"mapbox://tileset-source/test-user/hello-world"}', 200) + mock_request_post.return_value = MockResponse( + '{"id":"mapbox://tileset-source/test-user/hello-world"}', 200 + ) runner = CliRunner() - result = runner.invoke(add_source, ['test-user', 'hello-world', 'tests/fixtures/valid.ldgeojson']) + result = runner.invoke( + add_source, ["test-user", "hello-world", "tests/fixtures/valid.ldgeojson"] + ) assert result.exit_code == 0 - assert 'Validating tests/fixtures/valid.ldgeojson ...\n✔ valid' in result.output - assert 'Adding tests/fixtures/valid.ldgeojson to mapbox://tileset-source/test-user/hello-world' in result.output - assert '{\n "id": "mapbox://tileset-source/test-user/hello-world"\n}\n' in result.output + assert "Validating tests/fixtures/valid.ldgeojson ...\n✔ valid" in result.output + assert ( + "Adding tests/fixtures/valid.ldgeojson to mapbox://tileset-source/test-user/hello-world" + in result.output + ) + assert ( + '{\n "id": "mapbox://tileset-source/test-user/hello-world"\n}\n' + in result.output + ) @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_view_source(mock_request_get): - mock_request_get.return_value = MockResponse('{"id":"mapbox://tileset-source/test-user/hello-world"}', 200) + mock_request_get.return_value = MockResponse( + '{"id":"mapbox://tileset-source/test-user/hello-world"}', 200 + ) runner = CliRunner() - result = runner.invoke(view_source, ['test-user', 'hello-world']) + result = runner.invoke(view_source, ["test-user", "hello-world"]) assert result.exit_code == 0 - assert result.output == '{\n "id": "mapbox://tileset-source/test-user/hello-world"\n}\n' + assert ( + result.output + == '{\n "id": "mapbox://tileset-source/test-user/hello-world"\n}\n' + ) @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.delete') +@mock.patch("requests.delete") def test_cli_delete_source(mock_request_delete): - mock_request_delete.return_value = MockResponse('', 201) + mock_request_delete.return_value = MockResponse("", 201) runner = CliRunner() - result = runner.invoke(delete_source, ['test-user', 'hello-world']) + result = runner.invoke(delete_source, ["test-user", "hello-world"]) assert result.exit_code == 0 - assert result.output == 'Source deleted.\n' + assert result.output == "Source deleted.\n" @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_view_source(mock_request_get): mock_request_get.return_value = MockResponse( '[{"id":"mapbox://tileset-source/test-user/hello-world"},\ {"id":"mapbox://tileset-source/test-user/hola-mundo"}]', - 200 + 200, ) runner = CliRunner() - result = runner.invoke(list_sources, ['test-user']) + result = runner.invoke(list_sources, ["test-user"]) assert result.exit_code == 0 assert "mapbox://tileset-source/test-user/hello-world" in result.output assert "mapbox://tileset-source/test-user/hola-mundo" in result.output @@ -73,6 +90,6 @@ def test_cli_view_source(mock_request_get): @pytest.mark.usefixtures("token_environ") def test_cli_validate_source(): runner = CliRunner() - result = runner.invoke(validate_source, ['tests/fixtures/valid.ldgeojson']) + result = runner.invoke(validate_source, ["tests/fixtures/valid.ldgeojson"]) assert result.exit_code == 0 assert result.output == "Validating tests/fixtures/valid.ldgeojson ...\n✔ valid\n" diff --git a/tests/test_cli_status.py b/tests/test_cli_status.py index 7f784d3..fb9d7b2 100644 --- a/tests/test_cli_status.py +++ b/tests/test_cli_status.py @@ -6,34 +6,39 @@ from tilesets.cli import status -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text self.status_code = 200 + def MockResponse(self): return self @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_status(mock_request_get): runner = CliRunner() # sends expected request mock_request_get.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(status, ['test.id']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/status?access_token=fake-token') + result = runner.invoke(status, ["test.id"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/status?access_token=fake-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_status_use_token_flag(mock_request_get): runner = CliRunner() mock_request_get.return_value = MockResponse('{"message":"mock message"}') # Provides the flag --token - result = runner.invoke(status, ['test.id', '--token', 'flag-token']) - mock_request_get.assert_called_with('https://api.mapbox.com/tilesets/v1/test.id/status?access_token=flag-token') + result = runner.invoke(status, ["test.id", "--token", "flag-token"]) + mock_request_get.assert_called_with( + "https://api.mapbox.com/tilesets/v1/test.id/status?access_token=flag-token" + ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output diff --git a/tests/test_cli_update_recipe.py b/tests/test_cli_update_recipe.py index 1c0f54c..7050d34 100644 --- a/tests/test_cli_update_recipe.py +++ b/tests/test_cli_update_recipe.py @@ -5,9 +5,10 @@ from tilesets.cli import update_recipe -class MockResponse(): +class MockResponse: def __init__(self): self.status_code = 201 + def MockResponse(self): return self @@ -15,37 +16,40 @@ def MockResponse(self): @pytest.mark.usefixtures("token_environ") def test_cli_update_recipe_no_recipe(): runner = CliRunner() - result = runner.invoke(update_recipe, ['test.id', 'does/not/exist/recipe.json']) + result = runner.invoke(update_recipe, ["test.id", "does/not/exist/recipe.json"]) assert result.exit_code == 2 assert 'Path "does/not/exist/recipe.json" does not exist' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.patch') +@mock.patch("requests.patch") def test_cli_update_recipe(mock_request_patch): runner = CliRunner() # sends expected request mock_request_patch.return_value = MockResponse() - result = runner.invoke(update_recipe, ['test.id', 'tests/fixtures/recipe.json']) + result = runner.invoke(update_recipe, ["test.id", "tests/fixtures/recipe.json"]) mock_request_patch.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=fake-token', - json={'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'} + "https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=fake-token", + json={"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, ) assert result.exit_code == 0 - assert 'Updated recipe.' in result.output + assert "Updated recipe." in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.patch') +@mock.patch("requests.patch") def test_cli_update_recipe(mock_request_patch): runner = CliRunner() mock_request_patch.return_value = MockResponse() # Provides the flag --token - result = runner.invoke(update_recipe, ['test.id', 'tests/fixtures/recipe.json', '--token', 'flag-token']) + result = runner.invoke( + update_recipe, + ["test.id", "tests/fixtures/recipe.json", "--token", "flag-token"], + ) mock_request_patch.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=flag-token', - json={'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'} + "https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=flag-token", + json={"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, ) assert result.exit_code == 0 - assert 'Updated recipe.' in result.output + assert "Updated recipe." in result.output diff --git a/tests/test_cli_validate_recipe.py b/tests/test_cli_validate_recipe.py index ea4aede..0cf26dc 100644 --- a/tests/test_cli_validate_recipe.py +++ b/tests/test_cli_validate_recipe.py @@ -5,10 +5,11 @@ from tilesets.cli import validate_recipe -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text self.status_code = 200 + def MockResponse(self): return self @@ -16,37 +17,39 @@ def MockResponse(self): @pytest.mark.usefixtures("token_environ") def test_cli_validate_recipe_no_recipe(): runner = CliRunner() - result = runner.invoke(validate_recipe, ['does/not/exist/recipe.json']) + result = runner.invoke(validate_recipe, ["does/not/exist/recipe.json"]) assert result.exit_code == 2 assert 'Path "does/not/exist/recipe.json" does not exist' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.put') +@mock.patch("requests.put") def test_cli_validate_recipe(mock_request_put): runner = CliRunner() # sends expected request mock_request_put.return_value = MockResponse('{"message":"mock message"}') - result = runner.invoke(validate_recipe, ['tests/fixtures/recipe.json']) + result = runner.invoke(validate_recipe, ["tests/fixtures/recipe.json"]) mock_request_put.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/validateRecipe?access_token=fake-token', - json={'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'} + "https://api.mapbox.com/tilesets/v1/validateRecipe?access_token=fake-token", + json={"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.put') +@mock.patch("requests.put") def test_cli_validate_recipe_use_token_flag(mock_request_put): runner = CliRunner() mock_request_put.return_value = MockResponse('{"message":"mock message"}') # Provides the flag --token - result = runner.invoke(validate_recipe, ['tests/fixtures/recipe.json', '--token', 'flag-token']) + result = runner.invoke( + validate_recipe, ["tests/fixtures/recipe.json", "--token", "flag-token"] + ) mock_request_put.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/validateRecipe?access_token=flag-token', - json={'minzoom': 0, 'maxzoom': 10, 'layer_name': 'test_layer'} + "https://api.mapbox.com/tilesets/v1/validateRecipe?access_token=flag-token", + json={"minzoom": 0, "maxzoom": 10, "layer_name": "test_layer"}, ) assert result.exit_code == 0 assert '{\n "message": "mock message"\n}\n' in result.output diff --git a/tests/test_cli_view_recipe.py b/tests/test_cli_view_recipe.py index 9698b83..50ede39 100644 --- a/tests/test_cli_view_recipe.py +++ b/tests/test_cli_view_recipe.py @@ -5,38 +5,39 @@ from tilesets.cli import view_recipe -class MockResponse(): +class MockResponse: def __init__(self, mock_text): self.text = mock_text self.status_code = 200 + def MockResponse(self): return self @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_view_recipe(mock_request_get): runner = CliRunner() # sends expected request mock_request_get.return_value = MockResponse('{"fake":"recipe_data"}') - result = runner.invoke(view_recipe, ['test.id']) + result = runner.invoke(view_recipe, ["test.id"]) mock_request_get.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=fake-token' + "https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=fake-token" ) assert result.exit_code == 0 assert '{\n "fake": "recipe_data"\n}\n' in result.output @pytest.mark.usefixtures("token_environ") -@mock.patch('requests.get') +@mock.patch("requests.get") def test_cli_view_recipe_use_token_flag(mock_request_get): runner = CliRunner() mock_request_get.return_value = MockResponse('{"fake":"recipe_data"}') # Provides the flag --token - result = runner.invoke(view_recipe, ['test.id', '--token', 'flag-token']) + result = runner.invoke(view_recipe, ["test.id", "--token", "flag-token"]) mock_request_get.assert_called_with( - 'https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=flag-token' + "https://api.mapbox.com/tilesets/v1/test.id/recipe?access_token=flag-token" ) assert result.exit_code == 0 assert '{\n "fake": "recipe_data"\n}\n' in result.output diff --git a/tests/test_file_validator.py b/tests/test_file_validator.py index cbabed9..c05433f 100644 --- a/tests/test_file_validator.py +++ b/tests/test_file_validator.py @@ -5,22 +5,31 @@ filepath = os.path.join(os.path.dirname(__file__)) + def test_validate_ldgeojson(): runner = CliRunner() - result = runner.invoke(cli, ['validate-source', filepath + '/fixtures/valid.ldgeojson']) + result = runner.invoke( + cli, ["validate-source", filepath + "/fixtures/valid.ldgeojson"] + ) assert result.exit_code == 0 - assert '✔ valid' in result.output + assert "✔ valid" in result.output + def test_validate_invalid_ldgeojson(): runner = CliRunner() - result = runner.invoke(cli, ['validate-source', filepath + '/fixtures/invalid.ldgeojson']) + result = runner.invoke( + cli, ["validate-source", filepath + "/fixtures/invalid.ldgeojson"] + ) assert result.exit_code == 1 - output = 'Error: Invalid JSON on line 1 \n Invalid Content: None \n\n' + output = "Error: Invalid JSON on line 1 \n Invalid Content: None \n\n" assert output in result.output + def test_validate_invalid_geojson(): runner = CliRunner() - result = runner.invoke(cli, ['validate-source', filepath + '/fixtures/invalid-geojson.ldgeojson']) + result = runner.invoke( + cli, ["validate-source", filepath + "/fixtures/invalid-geojson.ldgeojson"] + ) assert result.exit_code == 1 output = "Error: Invalid geojson found on line 1 \n Invalid Feature: {'type': 'Feature', 'geometry': {'type': 'Point'}} \n Note - Geojson must be line delimited.\n" assert output in result.output diff --git a/tilesets/__init__.py b/tilesets/__init__.py index 341a9d9..2e7d59e 100644 --- a/tilesets/__init__.py +++ b/tilesets/__init__.py @@ -1,3 +1,3 @@ """tilesets package""" -__version__ = '0.2.0' +__version__ = "0.2.0" diff --git a/tilesets/cli.py b/tilesets/cli.py index 878ac54..7f8ffd7 100644 --- a/tilesets/cli.py +++ b/tilesets/cli.py @@ -18,15 +18,17 @@ def _get_token(token=None): if token is not None: return token else: - return os.environ.get('MAPBOX_ACCESS_TOKEN') or os.environ.get('MapboxAccessToken') + return os.environ.get("MAPBOX_ACCESS_TOKEN") or os.environ.get( + "MapboxAccessToken" + ) def _get_api(): """Get Mapbox tileset API base URL from environment""" - return os.environ.get('MAPBOX_API', "https://api.mapbox.com") + return os.environ.get("MAPBOX_API", "https://api.mapbox.com") -@click.version_option(version=tilesets.__version__, message='%(version)s') +@click.version_option(version=tilesets.__version__, message="%(version)s") @click.group() def cli(): """This is the command line interface for the Mapbox Tilesets API. @@ -37,13 +39,27 @@ def cli(): """ -@cli.command('create') -@click.argument('tileset', required=True, type=str) -@click.option('--recipe', '-r', required=True, type=click.Path(exists=True), help='path to a Recipe JSON document') -@click.option('--name', '-n', required=True, type=str, help='name of the tileset') -@click.option('--description', '-d', required=False, type=str, help='description of the tileset') -@click.option('--privacy', '-p', required=False, type=click.Choice(['public', 'private']), help='set the tileset privacy options') -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("create") +@click.argument("tileset", required=True, type=str) +@click.option( + "--recipe", + "-r", + required=True, + type=click.Path(exists=True), + help="path to a Recipe JSON document", +) +@click.option("--name", "-n", required=True, type=str, help="name of the tileset") +@click.option( + "--description", "-d", required=False, type=str, help="description of the tileset" +) +@click.option( + "--privacy", + "-p", + required=False, + type=click.Choice(["public", "private"]), + help="set the tileset privacy options", +) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def create(tileset, recipe, name=None, description=None, privacy=None, token=None): """Create a new tileset with a recipe. @@ -54,27 +70,30 @@ def create(tileset, recipe, name=None, description=None, privacy=None, token=Non """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) body = {} - body['name'] = name or '' - body['description'] = description or '' + body["name"] = name or "" + body["description"] = description or "" if privacy: - body['private'] = True if privacy == 'private' else False + body["private"] = True if privacy == "private" else False - if not '.' in tileset: - click.echo('Invalid tileset_id, format must match username.tileset') + if not "." in tileset: + click.echo("Invalid tileset_id, format must match username.tileset") sys.exit() if recipe: with open(recipe) as json_recipe: - body['recipe'] = json.load(json_recipe) + body["recipe"] = json.load(json_recipe) r = requests.post(url, json=body) utils.print_response(r.text) -@cli.command('publish') -@click.argument('tileset', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') + +@cli.command("publish") +@click.argument("tileset", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def publish(tileset, token=None): """Publish your tileset. @@ -82,18 +101,22 @@ def publish(tileset, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/publish?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}/publish?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) r = requests.post(url) if r.status_code == 200: utils.print_response(r.text) - click.echo(f'You can view the status of your tileset with the `tilesets status {tileset}` command.') + click.echo( + f"You can view the status of your tileset with the `tilesets status {tileset}` command." + ) else: utils.print_response(r.text) -@cli.command('status') -@click.argument('tileset', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("status") +@click.argument("tileset", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def status(tileset, token=None): """View the current queue/processing/complete status of your tileset. @@ -101,15 +124,17 @@ def status(tileset, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/status?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}/status?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) r = requests.get(url) utils.print_response(r.text) -@cli.command('jobs') -@click.argument('tileset', required=True, type=str) -@click.option('--stage', '-s', required=False, type=str, help='job stage') -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("jobs") +@click.argument("tileset", required=True, type=str) +@click.option("--stage", "-s", required=False, type=str, help="job stage") +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def jobs(tileset, stage, token=None): """View all jobs for a particular tileset. @@ -117,17 +142,21 @@ def jobs(tileset, stage, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/jobs?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}/jobs?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) if stage: - url = '{0}/tilesets/v1/{1}/jobs?stage={2}&access_token={3}'.format(mapbox_api, tileset, stage, mapbox_token) + url = "{0}/tilesets/v1/{1}/jobs?stage={2}&access_token={3}".format( + mapbox_api, tileset, stage, mapbox_token + ) r = requests.get(url) utils.print_response(r.text) -@cli.command('job') -@click.argument('tileset', required=True, type=str) -@click.argument('job_id', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("job") +@click.argument("tileset", required=True, type=str) +@click.argument("job_id", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def job(tileset, job_id, token=None): """View a single job for a particular tileset. @@ -135,15 +164,23 @@ def job(tileset, job_id, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/jobs/{2}?access_token={3}'.format(mapbox_api, tileset, job_id, mapbox_token) + url = "{0}/tilesets/v1/{1}/jobs/{2}?access_token={3}".format( + mapbox_api, tileset, job_id, mapbox_token + ) r = requests.get(url) utils.print_response(r.text) -@cli.command('list') -@click.argument('username', required=True, type=str) -@click.option('--verbose', '-v', required=False, is_flag=True, help='Will print all tileset information') -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("list") +@click.argument("username", required=True, type=str) +@click.option( + "--verbose", + "-v", + required=False, + is_flag=True, + help="Will print all tileset information", +) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def list(username, verbose, token=None): """List all tilesets for an account. By default the response is a simple list of tileset IDs. @@ -154,7 +191,9 @@ def list(username, verbose, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}?access_token={2}'.format(mapbox_api, username, mapbox_token) + url = "{0}/tilesets/v1/{1}?access_token={2}".format( + mapbox_api, username, mapbox_token + ) r = requests.get(url) if r.status_code == 200: if verbose: @@ -162,14 +201,14 @@ def list(username, verbose, token=None): else: j = json.loads(r.text) for tileset in j: - click.echo(tileset['id']) + click.echo(tileset["id"]) else: click.echo(r.text) -@cli.command('validate-recipe') -@click.argument('recipe', required=True, type=click.Path(exists=True)) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("validate-recipe") +@click.argument("recipe", required=True, type=click.Path(exists=True)) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def validate_recipe(recipe, token=None): """Validate a Recipe JSON document @@ -177,20 +216,22 @@ def validate_recipe(recipe, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/validateRecipe?access_token={1}'.format(mapbox_api, mapbox_token) + url = "{0}/tilesets/v1/validateRecipe?access_token={1}".format( + mapbox_api, mapbox_token + ) with open(recipe) as json_recipe: try: recipe_json = json.load(json_recipe) except: - click.echo('Error: recipe is not valid json') + click.echo("Error: recipe is not valid json") sys.exit() r = requests.put(url, json=recipe_json) utils.print_response(r.text) -@cli.command('view-recipe') -@click.argument('tileset', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("view-recipe") +@click.argument("tileset", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def view_recipe(tileset, token=None): """View a tileset's recipe JSON @@ -198,7 +239,9 @@ def view_recipe(tileset, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/recipe?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}/recipe?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) r = requests.get(url) if r.status_code == 200: utils.print_response(r.text) @@ -206,10 +249,10 @@ def view_recipe(tileset, token=None): click.echo(r.text) -@cli.command('update-recipe') -@click.argument('tileset', required=True, type=str) -@click.argument('recipe', required=True, type=click.Path(exists=True)) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("update-recipe") +@click.argument("tileset", required=True, type=str) +@click.argument("recipe", required=True, type=click.Path(exists=True)) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def update_recipe(tileset, recipe, token=None): """Update a Recipe JSON document for a particular tileset @@ -217,51 +260,66 @@ def update_recipe(tileset, recipe, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/{1}/recipe?access_token={2}'.format(mapbox_api, tileset, mapbox_token) + url = "{0}/tilesets/v1/{1}/recipe?access_token={2}".format( + mapbox_api, tileset, mapbox_token + ) with open(recipe) as json_recipe: try: recipe_json = json.load(json_recipe) except: - click.echo('Error: recipe is not valid json') + click.echo("Error: recipe is not valid json") sys.exit() r = requests.patch(url, json=recipe_json) if r.status_code == 201: - click.echo('Updated recipe.'); + click.echo("Updated recipe.") else: utils.print_response(r.text) -@cli.command('validate-source') -@click.argument('source_path', required=True, type=click.Path(exists=True)) +@cli.command("validate-source") +@click.argument("source_path", required=True, type=click.Path(exists=True)) def validate_source(source_path): """Validate your source file. $ tilesets validate-source """ line_count = 1 - with open(source_path, 'r') as inf: + with open(source_path, "r") as inf: click.echo("Validating {0} ...".format(source_path)) feature = None try: for feature in JSONSeqDecoder().decode(inf): utils.validate_geojson(feature) - line_count+=1 + line_count += 1 except JSONDecodeError: - click.echo("Error: Invalid JSON on line {} \n Invalid Content: {} \n".format(line_count, feature)) + click.echo( + "Error: Invalid JSON on line {} \n Invalid Content: {} \n".format( + line_count, feature + ) + ) sys.exit(1) except jsonschema.exceptions.ValidationError: - click.echo("Error: Invalid geojson found on line {} \n Invalid Feature: {} \n Note - Geojson must be line delimited.".format(line_count, feature)) + click.echo( + "Error: Invalid geojson found on line {} \n Invalid Feature: {} \n Note - Geojson must be line delimited.".format( + line_count, feature + ) + ) sys.exit(1) - click.echo('✔ valid') + click.echo("✔ valid") -@cli.command('add-source') -@click.argument('username', required=True, type=str) -@click.argument('id', required=True, type=str) -@click.argument('files', required=True, type=click.Path(exists=True, file_okay=True, dir_okay=True), nargs=-1) -@click.option('--no-validation', is_flag=True, help='Bypass source file validation') -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("add-source") +@click.argument("username", required=True, type=str) +@click.argument("id", required=True, type=str) +@click.argument( + "files", + required=True, + type=click.Path(exists=True, file_okay=True, dir_okay=True), + nargs=-1, +) +@click.option("--no-validation", is_flag=True, help="Bypass source file validation") +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") @click.pass_context def add_source(ctx, username, id, files, no_validation, token=None): """Create/add a tileset source @@ -271,15 +329,17 @@ def add_source(ctx, username, id, files, no_validation, token=None): mapbox_api = _get_api() mapbox_token = _get_token(token) for f in utils.flatten(files): - url = '{0}/tilesets/v1/sources/{1}/{2}?access_token={3}'.format(mapbox_api, username, id, mapbox_token) + url = "{0}/tilesets/v1/sources/{1}/{2}?access_token={3}".format( + mapbox_api, username, id, mapbox_token + ) if not no_validation: ctx.invoke(validate_source, source_path=f) - click.echo('Adding {0} to mapbox://tileset-source/{1}/{2}'.format(f, username, id)) + click.echo( + "Adding {0} to mapbox://tileset-source/{1}/{2}".format(f, username, id) + ) - r = requests.post(url, files={ - 'file': ('tileset-source', open(f, 'rb')) - }) + r = requests.post(url, files={"file": ("tileset-source", open(f, "rb"))}) if r.status_code == 200: utils.print_response(r.text) @@ -287,10 +347,10 @@ def add_source(ctx, username, id, files, no_validation, token=None): click.echo(r.text) -@cli.command('view-source') -@click.argument('username', required=True, type=str) -@click.argument('id', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("view-source") +@click.argument("username", required=True, type=str) +@click.argument("id", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def view_source(username, id, token=None): """View a Tileset Source's information @@ -298,7 +358,9 @@ def view_source(username, id, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/sources/{1}/{2}?access_token={3}'.format(mapbox_api, username, id, mapbox_token) + url = "{0}/tilesets/v1/sources/{1}/{2}?access_token={3}".format( + mapbox_api, username, id, mapbox_token + ) r = requests.get(url) if r.status_code == 200: utils.print_response(r.text) @@ -306,10 +368,10 @@ def view_source(username, id, token=None): click.echo(r.text) -@cli.command('delete-source') -@click.argument('username', required=True, type=str) -@click.argument('id', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("delete-source") +@click.argument("username", required=True, type=str) +@click.argument("id", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def delete_source(username, id, token=None): """Delete a Tileset Source + all of its files. @@ -317,17 +379,19 @@ def delete_source(username, id, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/sources/{1}/{2}?access_token={3}'.format(mapbox_api, username, id, mapbox_token) + url = "{0}/tilesets/v1/sources/{1}/{2}?access_token={3}".format( + mapbox_api, username, id, mapbox_token + ) r = requests.delete(url) if r.status_code == 201: - click.echo('Source deleted.') + click.echo("Source deleted.") else: utils.print_response(r.text) -@cli.command('list-sources') -@click.argument('username', required=True, type=str) -@click.option('--token', '-t', required=False, type=str, help='Mapbox access token') +@cli.command("list-sources") +@click.argument("username", required=True, type=str) +@click.option("--token", "-t", required=False, type=str, help="Mapbox access token") def list_sources(username, token=None): """List all Tileset Sources for an account. Response is an un-ordered array of sources. @@ -335,7 +399,9 @@ def list_sources(username, token=None): """ mapbox_api = _get_api() mapbox_token = _get_token(token) - url = '{0}/tilesets/v1/sources/{1}?access_token={2}'.format(mapbox_api, username, mapbox_token) + url = "{0}/tilesets/v1/sources/{1}?access_token={2}".format( + mapbox_api, username, mapbox_token + ) r = requests.get(url) if r.status_code == 200: utils.print_response(r.text) diff --git a/tilesets/scripts/utils.py b/tilesets/scripts/utils.py index 5352abe..26a0309 100644 --- a/tilesets/scripts/utils.py +++ b/tilesets/scripts/utils.py @@ -5,13 +5,15 @@ from functools import partial from jsonschema import validate -tileset_arg = click.argument('tileset', required=True, type=str) +tileset_arg = click.argument("tileset", required=True, type=str) + def absoluteFilePaths(directory): - for dirpath,_,filenames in os.walk(directory): + for dirpath, _, filenames in os.walk(directory): for f in filenames: yield os.path.abspath(os.path.join(dirpath, f)) + # takes a list of files or directories and converts # all directories into absolute file paths def flatten(files): @@ -25,68 +27,57 @@ def flatten(files): def print_response(text): try: - j = json.loads(text) - msg = json.dumps(j, indent=2, sort_keys=True) - click.echo(msg) + j = json.loads(text) + msg = json.dumps(j, indent=2, sort_keys=True) + click.echo(msg) except: - click.echo('Failure \n' + text) + click.echo("Failure \n" + text) def validate_geojson(feature): schema = { - "definitions": {}, - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://example.com/root.json", - "type": "object", - "title": "GeoJSON Schema", - "required": [ - "type", - "geometry", - "properties" - ], - "properties": { - "type": { - "$id": "#/properties/type", - "type": "string", - "title": "The Type Schema", - "default": "", - "examples": [ - "Feature" - ], - "pattern": "^(.*)$" - }, - "geometry": { - "$id": "#/properties/geometry", - "type": "object", - "title": "The Geometry Schema", - "required": [ - "type", - "coordinates" - ], - "properties": { + "definitions": {}, + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/root.json", + "type": "object", + "title": "GeoJSON Schema", + "required": ["type", "geometry", "properties"], + "properties": { "type": { - "$id": "#/properties/geometry/properties/type", - "type": "string", - "title": "The Type Schema", - "default": "", - "examples": [ - "Point" - ], - "pattern": "^(.*)$" + "$id": "#/properties/type", + "type": "string", + "title": "The Type Schema", + "default": "", + "examples": ["Feature"], + "pattern": "^(.*)$", + }, + "geometry": { + "$id": "#/properties/geometry", + "type": "object", + "title": "The Geometry Schema", + "required": ["type", "coordinates"], + "properties": { + "type": { + "$id": "#/properties/geometry/properties/type", + "type": "string", + "title": "The Type Schema", + "default": "", + "examples": ["Point"], + "pattern": "^(.*)$", + }, + "coordinates": { + "$id": "#/properties/geometry/properties/coordinates", + "type": "array", + "title": "The Coordinates Schema", + }, + }, + }, + "properties": { + "$id": "#/properties/properties", + "type": "object", + "title": "The Properties Schema", }, - "coordinates": { - "$id": "#/properties/geometry/properties/coordinates", - "type": "array", - "title": "The Coordinates Schema" - } - } }, - "properties": { - "$id": "#/properties/properties", - "type": "object", - "title": "The Properties Schema", - } - } } return validate(instance=feature, schema=schema) From a7b8550659adf7d03ade96a82236cdb4a0e15e70 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 11:27:24 -0700 Subject: [PATCH 02/12] adding pre commit hooks --- .pre-commit-config.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..677e2ba --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +repos: + - + repo: 'https://github.com/ambv/black' + # 18.6b1 + rev: ed50737290662f6ef4016a7ea44da78ee1eff1e2 + hooks: + - id: black + args: ['--safe'] + language_version: python3.6 + - + repo: 'https://github.com/pre-commit/pre-commit-hooks' + rev: v2.0.0 + hooks: + - id: flake8 + language_version: python3.6 + args: [ + # E501 let black handle all line length decisions + # W503 black conflicts with "line break before operator" rule + # E203 black conflicts with "whitespace before ':'" rule + # W605 false positive on our regex strings + '--ignore=E501,W503,E203,W605'] \ No newline at end of file From a91977d7cad29425e89401b7737078f9cf2c0ea2 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 11:30:24 -0700 Subject: [PATCH 03/12] run pre-commits w/ travis --- .travis.yml | 1 + setup.py | 49 ++++++++++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 131a997..2a3a943 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ install: script: - python --version - pytest --version + - pre-commit run --all-files - pytest --cov --cov-config=.coveragerc after_script: - echo "uploading to codecov" diff --git a/setup.py b/setup.py index 4ccf98c..0dc0b29 100644 --- a/setup.py +++ b/setup.py @@ -4,31 +4,38 @@ # Get the long description from the relevant file -with codecs_open('README.md', encoding='utf-8') as f: +with codecs_open("README.md", encoding="utf-8") as f: long_description = f.read() + def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() -setup(name='tilesets-cli', - version='0.2.0', - description=u"CLI for interacting with and preparing data for the Tilesets API", - long_description=long_description, - classifiers=[], - keywords='', - author=u"Mapbox", - author_email='sam@mapbox.com', - url='https://github.com/mapbox/tilesets-cli', - license='BSD-2', - packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), - install_requires=["boto3", "click~=7.0", "requests", "jsonschema~=3.0", "jsonseq~=1.0"], - include_package_data=True, - zip_safe=False, - extras_require={ - 'test': ['pytest>=3.6.0', 'pytest-cov'], - }, - entry_points=""" + +setup( + name="tilesets-cli", + version="0.2.0", + description=u"CLI for interacting with and preparing data for the Tilesets API", + long_description=long_description, + classifiers=[], + keywords="", + author=u"Mapbox", + author_email="sam@mapbox.com", + url="https://github.com/mapbox/tilesets-cli", + license="BSD-2", + packages=find_packages(exclude=["ez_setup", "examples", "tests"]), + install_requires=[ + "boto3", + "click~=7.0", + "requests", + "jsonschema~=3.0", + "jsonseq~=1.0", + ], + include_package_data=True, + zip_safe=False, + extras_require={"test": ["pytest>=3.6.0", "pytest-cov"]}, + entry_points=""" [console_scripts] tilesets=tilesets.cli:cli - """ - ) + """, +) From e953dbd668a42ed25aa4ac7ad38745169cb83098 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 15:14:10 -0700 Subject: [PATCH 04/12] few small fixes + adding pass for bare excepts --- .pre-commit-config.yaml | 4 ++-- tests/test_cli_create.py | 1 - tests/test_cli_sources.py | 2 +- tests/test_cli_update_recipe.py | 2 +- tilesets/cli.py | 2 +- tilesets/scripts/utils.py | 30 +++++++++++++++++++++++++----- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 677e2ba..dfdc174 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,5 +17,5 @@ repos: # E501 let black handle all line length decisions # W503 black conflicts with "line break before operator" rule # E203 black conflicts with "whitespace before ':'" rule - # W605 false positive on our regex strings - '--ignore=E501,W503,E203,W605'] \ No newline at end of file + # E722 bare excepts need to be addressed + '--ignore=E501,W503,E203,E722'] diff --git a/tests/test_cli_create.py b/tests/test_cli_create.py index 1067249..08a47ea 100644 --- a/tests/test_cli_create.py +++ b/tests/test_cli_create.py @@ -1,5 +1,4 @@ from unittest import mock -import json from click.testing import CliRunner import pytest diff --git a/tests/test_cli_sources.py b/tests/test_cli_sources.py index 8dca2bd..0045b78 100644 --- a/tests/test_cli_sources.py +++ b/tests/test_cli_sources.py @@ -74,7 +74,7 @@ def test_cli_delete_source(mock_request_delete): @pytest.mark.usefixtures("token_environ") @mock.patch("requests.get") -def test_cli_view_source(mock_request_get): +def test_cli_view_source_2(mock_request_get): mock_request_get.return_value = MockResponse( '[{"id":"mapbox://tileset-source/test-user/hello-world"},\ {"id":"mapbox://tileset-source/test-user/hola-mundo"}]', diff --git a/tests/test_cli_update_recipe.py b/tests/test_cli_update_recipe.py index 7050d34..59037d8 100644 --- a/tests/test_cli_update_recipe.py +++ b/tests/test_cli_update_recipe.py @@ -39,7 +39,7 @@ def test_cli_update_recipe(mock_request_patch): @pytest.mark.usefixtures("token_environ") @mock.patch("requests.patch") -def test_cli_update_recipe(mock_request_patch): +def test_cli_update_recipe2(mock_request_patch): runner = CliRunner() mock_request_patch.return_value = MockResponse() # Provides the flag --token diff --git a/tilesets/cli.py b/tilesets/cli.py index 7f8ffd7..321e06e 100644 --- a/tilesets/cli.py +++ b/tilesets/cli.py @@ -79,7 +79,7 @@ def create(tileset, recipe, name=None, description=None, privacy=None, token=Non if privacy: body["private"] = True if privacy == "private" else False - if not "." in tileset: + if utils.validate_tileset_id(tileset): click.echo("Invalid tileset_id, format must match username.tileset") sys.exit() diff --git a/tilesets/scripts/utils.py b/tilesets/scripts/utils.py index 26a0309..43c5541 100644 --- a/tilesets/scripts/utils.py +++ b/tilesets/scripts/utils.py @@ -1,8 +1,8 @@ import os import click import json -from json import JSONDecoder -from functools import partial +import re + from jsonschema import validate tileset_arg = click.argument("tileset", required=True, type=str) @@ -14,9 +14,10 @@ def absoluteFilePaths(directory): yield os.path.abspath(os.path.join(dirpath, f)) -# takes a list of files or directories and converts -# all directories into absolute file paths def flatten(files): + """takes a list of files or directories and converts + all directories into absolute file paths + """ for f in files: if os.path.isdir(f): for dir_file in absoluteFilePaths(f): @@ -30,10 +31,29 @@ def print_response(text): j = json.loads(text) msg = json.dumps(j, indent=2, sort_keys=True) click.echo(msg) - except: + except: # tofix: bare except click.echo("Failure \n" + text) +def validate_tileset_id(tileset_id): + """Assess if a Mapbox tileset_id is valid + + Parameters + ---------- + tileset_id: str + tileset_id of the form {account}.{tileset} + - account and tileset should each be under 32 characters + + Returns + ------- + is_valid: bool + boolean indicating if the tileset_id is valid + """ + pattern = r"^[a-z0-9-_]{1,32}\.[a-z0-9-_]{1,32}$" + + return re.match(pattern, tileset_id, flags=re.IGNORECASE) + + def validate_geojson(feature): schema = { "definitions": {}, From 6aed5cf336e6f83f1b1fb808881590f164113961 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 15:17:15 -0700 Subject: [PATCH 05/12] test dep for pre-commit --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0dc0b29..166f142 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def read(fname): ], include_package_data=True, zip_safe=False, - extras_require={"test": ["pytest>=3.6.0", "pytest-cov"]}, + extras_require={"test": ["pytest>=3.6.0", "pytest-cov", "pre-commit"]}, entry_points=""" [console_scripts] tilesets=tilesets.cli:cli From 33b7e42b589528b2410be7b03e6f144bd03c2d8a Mon Sep 17 00:00:00 2001 From: dnomadb Date: Wed, 11 Sep 2019 15:19:32 -0700 Subject: [PATCH 06/12] validate opposite --- tilesets/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tilesets/cli.py b/tilesets/cli.py index 321e06e..2566d2c 100644 --- a/tilesets/cli.py +++ b/tilesets/cli.py @@ -79,7 +79,7 @@ def create(tileset, recipe, name=None, description=None, privacy=None, token=Non if privacy: body["private"] = True if privacy == "private" else False - if utils.validate_tileset_id(tileset): + if not utils.validate_tileset_id(tileset): click.echo("Invalid tileset_id, format must match username.tileset") sys.exit() From 98ee29d30b4d6cb12ec897362beb9f88b204899b Mon Sep 17 00:00:00 2001 From: dnomadb Date: Mon, 16 Sep 2019 08:49:42 -0700 Subject: [PATCH 07/12] update version --- CHANGELOG.md | 6 +++++- tilesets/__init__.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 035f218..e734b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ +## 0.2.1 (20190-09-16) +- Reformatting using `black` +- More robust tileset id checking + ## 0.2.0 (2019-09-11) -- Add `tilesets list ` command +- Add `tilesets list ` command ## 0.1.0 (2019-08-01) diff --git a/tilesets/__init__.py b/tilesets/__init__.py index 2e7d59e..977883e 100644 --- a/tilesets/__init__.py +++ b/tilesets/__init__.py @@ -1,3 +1,3 @@ """tilesets package""" -__version__ = "0.2.0" +__version__ = "0.2.1" From aecaced1bd049d347e13aafe0f374debfc7f3444 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Mon, 16 Sep 2019 08:54:39 -0700 Subject: [PATCH 08/12] update contributing --- CONTRIBUTING.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fd09ed..56cb850 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,47 @@ Releases are simply tags on GitHub. Once changes have been merged to master: 1. Update the changelog 1. Tag on github with `git tag`. For example `git tag -a v0.2.0 -m 'v0.2.0'` +## Installation +First, clone the repo and `cd` into the folder: +``` +$ git clone git@github.com:mapbox/tilesets-cli.git +$ cd tilesets-cli +``` +Then, in a virtual environment, install the module with `[test]` extras: +``` +pip install -e '.[test]' +``` +## Pre-commit hooks +We use [pre-commit hooks](https://pre-commit.com/) to auto-format and validate code before committing. `pre-commit` is included with the `[test]` extras, but you must run: +``` +$ pre-commit install +``` +within the repo to have the actions specified in `.pre-commit-config.yaml`. + +After this, when commit, you'll see: +``` +git commit -m 'update version' +black....................................................................Passed +Flake8...................................................................Passed +``` +If your pre-commit hooks ran successfully. Note that `black` modifies your code, which means that if there is a syntax error you'll first see: +``` +git commit -m '{message}' +black....................................................................Failed +hookid: black + +Files were modified by this hook. Additional output: + +reformatted this/file/was/reformatted.py +All done! ✨ 🍰 ✨ +1 file reformatted. + +Flake8...................................................................Failed +hookid: flake8 + +this/file/was/reformatted.py:{line}:{character}: {what is incorrect} +``` + ## Tests All tests are runnable with pytest. pytest is not installed by default and can be installed with the pip test extras From d843107d56a75bb831ce0c0893690597de473cbc Mon Sep 17 00:00:00 2001 From: dnomadb Date: Mon, 16 Sep 2019 08:58:13 -0700 Subject: [PATCH 09/12] update CONTRIBUTING --- CONTRIBUTING.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 56cb850..09fcd7d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,6 @@ ## Welcome -Hi there! Welcome to the tilesets-cli contributing document. Issues, comments, and pull requests are welcome. Please tag @mapsam, @dianeschulze, and @millzpaugh for any questions or reviews. - -## Release process - -Releases are simply tags on GitHub. Once changes have been merged to master: - -1. Update the changelog -1. Tag on github with `git tag`. For example `git tag -a v0.2.0 -m 'v0.2.0'` +Hi there! Welcome to the tilesets-cli contributing document. Issues, comments, and pull requests are welcome. Please tag @mapsam, @dianeschulze, and @dnomadb for any questions or reviews. ## Installation First, clone the repo and `cd` into the folder: @@ -24,15 +17,15 @@ We use [pre-commit hooks](https://pre-commit.com/) to auto-format and validate c ``` $ pre-commit install ``` -within the repo to have the actions specified in `.pre-commit-config.yaml`. +within the repo to have the actions specified in `.pre-commit-config.yaml` registered. -After this, when commit, you'll see: +After this, when committing, you'll see: ``` git commit -m 'update version' black....................................................................Passed Flake8...................................................................Passed ``` -If your pre-commit hooks ran successfully. Note that `black` modifies your code, which means that if there is a syntax error you'll first see: +If your pre-commit hooks ran successfully. Note that `black` modifies your code, which means that if there is a syntax error you'll first see something like: ``` git commit -m '{message}' black....................................................................Failed @@ -47,8 +40,16 @@ All done! ✨ 🍰 ✨ Flake8...................................................................Failed hookid: flake8 -this/file/was/reformatted.py:{line}:{character}: {what is incorrect} +this/file/was/reformatted.py:{line}:{character}: {what was incorrect} ``` +After which you can add these changes and commit again. Note that failing pre-commit commands mean that the commit has not taken place: you must commit again! + +## Release process + +Releases are simply tags on GitHub. Once changes have been merged to master: + +1. Update the changelog +1. Tag on github with `git tag`. For example `git tag -a v0.2.0 -m 'v0.2.0'` ## Tests From 3e4d24cdd40928d86ba3821c0c034bfaabf2961a Mon Sep 17 00:00:00 2001 From: dnomadb Date: Mon, 16 Sep 2019 14:57:25 -0700 Subject: [PATCH 10/12] extras commmands --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 166f142..19cb842 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,9 @@ def read(fname): ], include_package_data=True, zip_safe=False, - extras_require={"test": ["pytest>=3.6.0", "pytest-cov", "pre-commit"]}, + extras_require={ + "test": ["pytest>=3.6.0", "pytest-cov", "pre-commit", "black", "pep8"] + }, entry_points=""" [console_scripts] tilesets=tilesets.cli:cli From 2de18ab7061d00e2483191a55cd140b1978036b5 Mon Sep 17 00:00:00 2001 From: dnomadb Date: Mon, 16 Sep 2019 15:03:40 -0700 Subject: [PATCH 11/12] dont ovverrise language version --- .pre-commit-config.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dfdc174..3d747cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,13 +6,11 @@ repos: hooks: - id: black args: ['--safe'] - language_version: python3.6 - repo: 'https://github.com/pre-commit/pre-commit-hooks' rev: v2.0.0 hooks: - id: flake8 - language_version: python3.6 args: [ # E501 let black handle all line length decisions # W503 black conflicts with "line break before operator" rule From 03edddc345b919fb9c77975619fa2a768aae06bc Mon Sep 17 00:00:00 2001 From: mapsam Date: Tue, 17 Sep 2019 14:28:04 -0700 Subject: [PATCH 12/12] 2 4 6 pep8, pre-commit is really great --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d747cb..2a2b581 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ repos: - repo: 'https://github.com/ambv/black' # 18.6b1 - rev: ed50737290662f6ef4016a7ea44da78ee1eff1e2 + rev: stable hooks: - id: black args: ['--safe']