diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d0edfe7f6..1ff5c6551a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,6 +41,28 @@ jobs: . env/bin/activate python -m black --exclude '(env|venv|.eggs)' --check . + check_compatibility: + parameters: + python_version: + type: string + docker: + - image: cimg/python:3.10 + steps: + - checkout + - run: + name: Check if requirements files have changed + command: ./scripts/check_requirements_changes.sh + - run: + name: Install dependencies and Check compatibility + command: | + if [ "$REQUIREMENTS_CHANGED" == "true" ]; then + sudo apt-get update + sudo apt-get install -y jq curl + ./scripts/check_compatibility.sh << parameters.python_version >> + else + echo "Skipping compatibility checks..." + fi + build-and-test: resource_class: medium parallelism: 2 @@ -204,6 +226,18 @@ jobs: workflows: + compatibility_checks: + jobs: + - check_compatibility: + python_version: "3.9" + name: check-compatibility-3.9 + - check_compatibility: + python_version: "3.10" + name: check-compatibility-3.10 + - check_compatibility: + python_version: "3.11" + name: check-compatibility-3.11 + pr-requirements: jobs: - black: diff --git a/bittensor/commands/overview.py b/bittensor/commands/overview.py index 27278349c6..db3a9000b0 100644 --- a/bittensor/commands/overview.py +++ b/bittensor/commands/overview.py @@ -18,11 +18,12 @@ import argparse import bittensor from tqdm import tqdm +from concurrent.futures import ProcessPoolExecutor from fuzzywuzzy import fuzz from rich.align import Align from rich.table import Table from rich.prompt import Prompt -from typing import List, Optional, Dict +from typing import List, Optional, Dict, Tuple from .utils import ( get_hotkey_wallets_for_wallet, get_coldkey_wallets_for_path, @@ -95,7 +96,7 @@ def run(cli): return # Pull neuron info for all keys. - neurons: Dict[str, List[bittensor.NeuronInfoLite, bittensor.wallet]] = {} + neurons: Dict[str, List[bittensor.NeuronInfoLite, str]] = {} block = subtensor.block netuids = subtensor.get_all_subnet_netuids() @@ -110,21 +111,32 @@ def run(cli): cli.config.subtensor.get("network", defaults.subtensor.network) ) ): - for netuid in tqdm(netuids_copy, desc="Checking each subnet"): - all_neurons: List[bittensor.NeuronInfoLite] = subtensor.neurons_lite( - netuid=netuid + hotkey_addr_to_wallet = { + hotkey.hotkey.ss58_address: hotkey for hotkey in all_hotkeys + } + all_hotkey_addresses = list(hotkey_addr_to_wallet.keys()) + + # Pull neuron info for all keys. + ## Max len(netuids) or 5 threads. + with ProcessPoolExecutor(max_workers=max(len(netuids), 5)) as executor: + results = executor.map( + OverviewCommand._get_neurons_for_netuid, + [(cli.config, netuid, all_hotkey_addresses) for netuid in netuids], ) - # Map the hotkeys to uids - hotkey_to_neurons = {n.hotkey: n.uid for n in all_neurons} - for hot_wallet in all_hotkeys: - uid = hotkey_to_neurons.get(hot_wallet.hotkey.ss58_address) - if uid is not None: - nn = all_neurons[uid] - neurons[str(netuid)].append((nn, hot_wallet)) - - if len(neurons[str(netuid)]) == 0: - # Remove netuid from overview if no neurons are found. - netuids.remove(netuid) + executor.shutdown(wait=True) # wait for all complete + + for result in results: + netuid, neurons_result, err_msg = result + if err_msg is not None: + console.print(err_msg) + + if len(neurons_result) == 0: + # Remove netuid from overview if no neurons are found. + netuids.remove(netuid) + del neurons[str(netuid)] + else: + # Add neurons to overview. + neurons[str(netuid)] = neurons_result # Setup outer table. grid = Table.grid(pad_edge=False) @@ -156,7 +168,8 @@ def run(cli): total_dividends = 0.0 total_emission = 0 - for nn, hotwallet in neurons[str(netuid)]: + for nn, hotwallet_addr in neurons[str(netuid)]: + hotwallet = hotkey_addr_to_wallet[hotwallet_addr] nn: bittensor.NeuronInfoLite uid = nn.uid active = nn.active @@ -373,6 +386,32 @@ def overview_sort_function(row): # Print the entire table/grid console.print(grid, width=cli.config.get("width", None)) + @staticmethod + def _get_neurons_for_netuid( + args_tuple: Tuple["bittensor.Config", int, List[str]] + ) -> Tuple[int, List[Tuple["bittensor.NeuronInfoLite", str]], Optional[str]]: + subtensor_config, netuid, hot_wallets = args_tuple + + result: List[Tuple["bittensor.NeuronInfoLite", str]] = [] + + try: + subtensor = bittensor.subtensor(config=subtensor_config) + + all_neurons: List["bittensor.NeuronInfoLite"] = subtensor.neurons_lite( + netuid=netuid + ) + # Map the hotkeys to uids + hotkey_to_neurons = {n.hotkey: n.uid for n in all_neurons} + for hot_wallet_addr in hot_wallets: + uid = hotkey_to_neurons.get(hot_wallet_addr) + if uid is not None: + nn = all_neurons[uid] + result.append((nn, hot_wallet_addr)) + except Exception as e: + return netuid, [], "Error: {}".format(e) + + return netuid, result, None + @staticmethod def add_args(parser: argparse.ArgumentParser): overview_parser = parser.add_parser( diff --git a/bittensor/extrinsics/serving.py b/bittensor/extrinsics/serving.py index 90806f7ff3..4bdfde7f76 100644 --- a/bittensor/extrinsics/serving.py +++ b/bittensor/extrinsics/serving.py @@ -143,7 +143,6 @@ def serve_axon_extrinsic( subtensor: "bittensor.subtensor", netuid: int, axon: "bittensor.Axon", - use_upnpc: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, prompt: bool = False, @@ -154,9 +153,6 @@ def serve_axon_extrinsic( The netuid being served on. axon (bittensor.Axon): Axon to serve. - use_upnpc (:type:bool, `optional`): - If true, the axon attempts port forward through your router before - subscribing. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true, or returns false if the extrinsic fails to enter the block within the timeout. @@ -172,30 +168,7 @@ def serve_axon_extrinsic( """ axon.wallet.hotkey axon.wallet.coldkeypub - - # ---- Setup UPNPC ---- - if use_upnpc: - if prompt: - if not Confirm.ask("Attempt port forwarding with upnpc?"): - return False - try: - external_port = net.upnpc_create_port_map(port=axon.port) - bittensor.__console__.print( - ":white_heavy_check_mark: [green]Forwarded port: {}[/green]".format( - axon.port - ) - ) - bittensor.logging.success( - prefix="Forwarded port", sufix="{}".format(axon.port) - ) - except net.UPNPCException as upnpc_exception: - raise RuntimeError( - "Failed to hole-punch with upnpc with exception {}".format( - upnpc_exception - ) - ) from upnpc_exception - else: - external_port = axon.external_port + external_port = axon.external_port # ---- Get external ip ---- if axon.external_ip == None: diff --git a/bittensor/mock/subtensor_mock.py b/bittensor/mock/subtensor_mock.py index 4ca111921d..d41b4afde4 100644 --- a/bittensor/mock/subtensor_mock.py +++ b/bittensor/mock/subtensor_mock.py @@ -671,7 +671,7 @@ def query_constant( if state_at_block is not None: return SimpleNamespace(value=state_at_block) - return state_at_block # Can be None + return state_at_block["data"]["free"] # Can be None else: return None diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 84ff2318a5..7271c2c868 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -716,13 +716,12 @@ def serve_axon( self, netuid: int, axon: "bittensor.Axon", - use_upnpc: bool = False, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, prompt: bool = False, ) -> bool: return serve_axon_extrinsic( - self, netuid, axon, use_upnpc, wait_for_inclusion, wait_for_finalization + self, netuid, axon, wait_for_inclusion, wait_for_finalization ) def _do_serve_axon( @@ -2225,32 +2224,6 @@ def _do_nominate( else: raise NominationError(response.error_message) - def weights( - self, netuid: int, block: Optional[int] = None - ) -> List[Tuple[int, List[Tuple[int, int]]]]: - w_map = [] - w_map_encoded = self.query_map_subtensor( - name="Weights", block=block, params=[netuid] - ) - if w_map_encoded.records: - for uid, w in w_map_encoded: - w_map.append((uid.serialize(), w.serialize())) - - return w_map - - def bonds( - self, netuid: int, block: Optional[int] = None - ) -> List[Tuple[int, List[Tuple[int, int]]]]: - b_map = [] - b_map_encoded = self.query_map_subtensor( - name="Bonds", block=block, params=[netuid] - ) - if b_map_encoded.records: - for uid, b in b_map_encoded: - b_map.append((uid.serialize(), b.serialize())) - - return b_map - ################ #### Legacy #### ################ diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 1126b37d05..4dfad57750 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -60,22 +60,31 @@ def unbiased_topk(values, k, dim=0, sorted=True, largest=True): return topk, permutation[indices] -def version_checking(): - response = requests.get(bittensor.__pipaddress__) - latest_version = response.json()["info"]["version"] - version_split = latest_version.split(".") - latest_version_as_int = ( - (100 * int(version_split[0])) - + (10 * int(version_split[1])) - + (1 * int(version_split[2])) - ) +def version_checking(timeout: int = 15): + try: + bittensor.logging.info( + f"Checking latest Bittensor version at: {bittensor.__pipaddress__}" + ) + response = requests.get(bittensor.__pipaddress__, timeout=timeout) + latest_version = response.json()["info"]["version"] + version_split = latest_version.split(".") + latest_version_as_int = ( + (100 * int(version_split[0])) + + (10 * int(version_split[1])) + + (1 * int(version_split[2])) + ) - if latest_version_as_int > bittensor.__version_as_int__: - print( - "\u001b[33mBittensor Version: Current {}/Latest {}\nPlease update to the latest version at your earliest convenience\u001b[0m".format( - bittensor.__version__, latest_version + if latest_version_as_int > bittensor.__version_as_int__: + print( + "\u001b[33mBittensor Version: Current {}/Latest {}\nPlease update to the latest version at your earliest convenience\u001b[0m".format( + bittensor.__version__, latest_version + ) ) - ) + + except requests.exceptions.Timeout: + bittensor.logging.error("Version check failed due to timeout") + except requests.exceptions.RequestException as e: + bittensor.logging.error(f"Version check failed due to request failure: {e}") def strtobool_with_default( diff --git a/bittensor/utils/networking.py b/bittensor/utils/networking.py index ab2e698f85..d8e1b79e9f 100644 --- a/bittensor/utils/networking.py +++ b/bittensor/utils/networking.py @@ -24,7 +24,6 @@ import json import netaddr import requests -import miniupnpc from loguru import logger @@ -156,73 +155,6 @@ def get_external_ip() -> str: raise ExternalIPNotFound -class UPNPCException(Exception): - """Raised when trying to perform a port mapping on your router.""" - - -def upnpc_create_port_map(port: int): - r"""Creates a upnpc port map on your router from passed external_port to local port. - - Args: - port (int, `required`): - The local machine port to map from your external port. - - Return: - external_port (int, `required`): - The external port mapped to the local port on your machine. - - Raises: - UPNPCException (Exception): - Raised if UPNPC port mapping fails, for instance, if upnpc is not enabled on your router. - """ - try: - upnp = miniupnpc.UPnP() - upnp.discoverdelay = 200 - logger.debug("UPNPC: Using UPnP to open a port on your router ...") - logger.debug("UPNPC: Discovering... delay={}ms", upnp.discoverdelay) - ndevices = upnp.discover() - upnp.selectigd() - logger.debug("UPNPC: " + str(ndevices) + " device(s) detected") - - ip = upnp.lanaddr - external_ip = upnp.externalipaddress() - - logger.debug("UPNPC: your local ip address: " + str(ip)) - logger.debug("UPNPC: your external ip address: " + str(external_ip)) - logger.debug( - "UPNPC: status = " - + str(upnp.statusinfo()) - + " connection type = " - + str(upnp.connectiontype()) - ) - - # find a free port for the redirection - external_port = port - rc = upnp.getspecificportmapping(external_port, "TCP") - while rc != None and external_port < 65536: - external_port += 1 - rc = upnp.getspecificportmapping(external_port, "TCP") - if rc != None: - raise UPNPCException("UPNPC: No available external ports for port mapping.") - - logger.info( - "UPNPC: trying to redirect remote: {}:{} => local: {}:{} over TCP", - external_ip, - external_port, - ip, - port, - ) - upnp.addportmapping( - external_port, "TCP", ip, port, "Bittensor: %u" % external_port, "" - ) - logger.info("UPNPC: Create Success") - - return external_port - - except Exception as e: - raise UPNPCException(e) from e - - def get_formatted_ws_endpoint_url(endpoint_url: str) -> str: """ Returns a formatted websocket endpoint url. diff --git a/contrib/CODE_REVIEW_DOCS.md b/contrib/CODE_REVIEW_DOCS.md new file mode 100644 index 0000000000..9909606a89 --- /dev/null +++ b/contrib/CODE_REVIEW_DOCS.md @@ -0,0 +1,72 @@ +# Code Review +### Conceptual Review + +A review can be a conceptual review, where the reviewer leaves a comment + * `Concept (N)ACK`, meaning "I do (not) agree with the general goal of this pull + request", + * `Approach (N)ACK`, meaning `Concept ACK`, but "I do (not) agree with the + approach of this change". + +A `NACK` needs to include a rationale why the change is not worthwhile. +NACKs without accompanying reasoning may be disregarded. +After conceptual agreement on the change, code review can be provided. A review +begins with `ACK BRANCH_COMMIT`, where `BRANCH_COMMIT` is the top of the PR +branch, followed by a description of how the reviewer did the review. The +following language is used within pull request comments: + + - "I have tested the code", involving change-specific manual testing in + addition to running the unit, functional, or fuzz tests, and in case it is + not obvious how the manual testing was done, it should be described; + - "I have not tested the code, but I have reviewed it and it looks + OK, I agree it can be merged"; + - A "nit" refers to a trivial, often non-blocking issue. + +### Code Review +Project maintainers reserve the right to weigh the opinions of peer reviewers +using common sense judgement and may also weigh based on merit. Reviewers that +have demonstrated a deeper commitment and understanding of the project over time +or who have clear domain expertise may naturally have more weight, as one would +expect in all walks of life. + +Where a patch set affects consensus-critical code, the bar will be much +higher in terms of discussion and peer review requirements, keeping in mind that +mistakes could be very costly to the wider community. This includes refactoring +of consensus-critical code. + +Where a patch set proposes to change the Bittensor consensus, it must have been +discussed extensively on the discord server and other channels, be accompanied by a widely +discussed BIP and have a generally widely perceived technical consensus of being +a worthwhile change based on the judgement of the maintainers. + +### Finding Reviewers + +As most reviewers are themselves developers with their own projects, the review +process can be quite lengthy, and some amount of patience is required. If you find +that you've been waiting for a pull request to be given attention for several +months, there may be a number of reasons for this, some of which you can do something +about: + + - It may be because of a feature freeze due to an upcoming release. During this time, + only bug fixes are taken into consideration. If your pull request is a new feature, + it will not be prioritized until after the release. Wait for the release. + - It may be because the changes you are suggesting do not appeal to people. Rather than + nits and critique, which require effort and means they care enough to spend time on your + contribution, thundering silence is a good sign of widespread (mild) dislike of a given change + (because people don't assume *others* won't actually like the proposal). Don't take + that personally, though! Instead, take another critical look at what you are suggesting + and see if it: changes too much, is too broad, doesn't adhere to the + [developer notes](DEVELOPMENT_WORKFLOW.md), is dangerous or insecure, is messily written, etc. + Identify and address any of the issues you find. Then ask e.g. on IRC if someone could give + their opinion on the concept itself. + - It may be because your code is too complex for all but a few people, and those people + may not have realized your pull request even exists. A great way to find people who + are qualified and care about the code you are touching is the + [Git Blame feature](https://docs.github.com/en/github/managing-files-in-a-repository/managing-files-on-github/tracking-changes-in-a-file). Simply + look up who last modified the code you are changing and see if you can find + them and give them a nudge. Don't be incessant about the nudging, though. + - Finally, if all else fails, ask on IRC or elsewhere for someone to give your pull request + a look. If you think you've been waiting for an unreasonably long time (say, + more than a month) for no particular reason (a few lines changed, etc.), + this is totally fine. Try to return the favor when someone else is asking + for feedback on their code, and the universe balances out. + - Remember that the best thing you can do while waiting is give review to others! \ No newline at end of file diff --git a/contrib/CONTRIBUTING.md b/contrib/CONTRIBUTING.md index cb04527a52..628c52614a 100644 --- a/contrib/CONTRIBUTING.md +++ b/contrib/CONTRIBUTING.md @@ -3,9 +3,11 @@ The following is a set of guidelines for contributing to Bittensor, which are hosted in the [Opentensor Organization](https://github.com/opentensor) on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. ## Table Of Contents - 1. [I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) 1. [What should I know before I get started?](#what-should-i-know-before-i-get-started) +1. [Getting Started](#getting-started) + 1. [Good First Issue Label](#good-first-issue-label) + 1. [Beginner and Help-wanted Issues Label](#beginner-and-help-wanted-issues-label) 1. [How Can I Contribute?](#how-can-i-contribute) 1. [Code Contribution General Guideline](#code-contribution-general-guidelines) 1. [Pull Request Philosophy](#pull-request-philosophy) @@ -14,6 +16,7 @@ The following is a set of guidelines for contributing to Bittensor, which are ho 1. [Addressing Feedback](#addressing-feedback) 1. [Squashing Commits](#squashing-commits) 1. [Refactoring](#refactoring) + 1. [Peer Review](#peer-review) 1. [Reporting Bugs](#reporting-bugs) 1. [Suggesting Features](#suggesting-enhancements) @@ -34,6 +37,33 @@ Additionally, note that the core implementation of Bittensor consists of two sep Supplemental, yet necessary repositories are [openvalidators](https://github.com/opentensor/validators) and [openminers](https://github.com/opentensor/miners) which contain Bittensor Validators and Miners (respectively) designed by the OpenTensor Foundation team and open-sourced for the community to use. +## Getting Started +New contributors are very welcome and needed. +Reviewing and testing is highly valued and the most effective way you can contribute as a new contributor. It also will teach you much more about the code and process than opening pull requests. + +Before you start contributing, familiarize yourself with the Bittensor Core build system and tests. Refer to the documentation in the repository on how to build Bittensor core and how to run the unit tests, functional tests. + +There are many open issues of varying difficulty waiting to be fixed. If you're looking for somewhere to start contributing, check out the [good first issue](https://github.com/opentensor/bittensor/labels/good%20first%20issue) list or changes that are up for grabs. Some of them might no longer be applicable. So if you are interested, but unsure, you might want to leave a comment on the issue first. +### Good First Issue Label +The purpose of the good first issue label is to highlight which issues are suitable for a new contributor without a deep understanding of the codebase. + +However, good first issues can be solved by anyone. If they remain unsolved for a longer time, a frequent contributor might address them. + +You do not need to request permission to start working on an issue. However, you are encouraged to leave a comment if you are planning to work on it. This will help other contributors monitor which issues are actively being addressed and is also an effective way to request assistance if and when you need it. + +### Beginner and Help-wanted Issues Label +You can start by looking through these `beginner` and `help-wanted` issues: + +* [Beginner issues](https://github.com/opentensor/bittensor/labels/beginner) - issues which should only require a few lines of code, and a test or two. +* [Help wanted issues](https://github.com/opentensor/bittensor/labels/help%20wanted) - issues which should be a bit more involved than `beginner` issues. + +## Communication Channels +Most communication about Bittensor development happens on Discord channel. +Here's the link of Discord community. +[Bittensor Discord](https://discord.com/channels/799672011265015819/799672011814862902) + +And also here. +[Bittensor Community Discord](https://discord.com/channels/1120750674595024897/1120799375703162950) ## How Can I Contribute? @@ -65,7 +95,6 @@ Here is a high-level summary: If you're looking to contribute to Bittensor but unsure where to start, please join our community [discord](https://discord.gg/bittensor), a developer-friendly Bittensor town square. Start with [#development](https://discord.com/channels/799672011265015819/799678806159392768) and [#bounties](https://discord.com/channels/799672011265015819/1095684873810890883) to see what issues are currently posted. For a greater understanding of Bittensor's usage and development, check the [Bittensor Documentation](https://bittensor.com/docs). - #### Pull Request Philosophy Patchsets and enhancements should always be focused. A pull request could add a feature, fix a bug, or refactor code, but it should not contain a mixture of these. Please also avoid 'super' pull requests which attempt to do too much, are overly large, or overly complex as this makes review difficult. @@ -80,7 +109,6 @@ Specifically, pull requests must adhere to the following criteria: - If your changes are extensive, consider breaking your PR into smaller, related PRs. This makes your contributions easier to understand and review. - Be active in the discussion about your PR. Respond promptly to comments and questions to help reviewers understand your changes and speed up the acceptance process. - Generally, all pull requests must: - Have a clear use case, fix a demonstrable bug or serve the greater good of the project (e.g. refactoring for modularisation). @@ -94,7 +122,6 @@ Generally, all pull requests must: Please follow these steps to have your contribution considered by the maintainers: - *Before* creating the PR: 1. Read the [development workflow](./DEVELOPMENT_WORKFLOW.md) defined for this repository to understand our workflow. 2. Ensure your PR meets the criteria stated in the 'Pull Request Philosophy' section. @@ -146,7 +173,6 @@ Here is a quick summary: Remember, testing is crucial for maintaining code health, catching issues early, and facilitating the addition of new features or refactoring of existing code. - #### Addressing Feedback After submitting your pull request, expect comments and reviews from other contributors. You can add more commits to your pull request by committing them locally and pushing to your fork. @@ -183,6 +209,21 @@ Project maintainers aim for a quick turnaround on refactoring pull requests, so Pull requests that refactor the code should not be made by new contributors. It requires a certain level of experience to know where the code belongs to and to understand the full ramification (including rebase effort of open pull requests). Trivial pull requests or pull requests that refactor the code with no clear benefits may be immediately closed by the maintainers to reduce unnecessary workload on reviewing. +#### Peer Review + +Anyone may participate in peer review which is expressed by comments in the pull request. Typically reviewers will review the code for obvious errors, as well as test out the patch set and opine on the technical merits of the patch. Project maintainers take into account the peer review when determining if there is consensus to merge a pull request (remember that discussions may have taken place elsewhere, not just on GitHub). The following language is used within pull-request comments: + +- ACK means "I have tested the code and I agree it should be merged"; +- NACK means "I disagree this should be merged", and must be accompanied by sound technical justification. NACKs without accompanying reasoning may be disregarded; +- utACK means "I have not tested the code, but I have reviewed it and it looks OK, I agree it can be merged"; +- Concept ACK means "I agree in the general principle of this pull request"; +- Nit refers to trivial, often non-blocking issues. + +Reviewers should include the commit(s) they have reviewed in their comments. This can be done by copying the commit SHA1 hash. + +A pull request that changes consensus-critical code is considerably more involved than a pull request that adds a feature to the wallet, for example. Such patches must be reviewed and thoroughly tested by several reviewers who are knowledgeable about the changed subsystems. Where new features are proposed, it is helpful for reviewers to try out the patch set on a test network and indicate that they have done so in their review. Project maintainers will take this into consideration when merging changes. + +For a more detailed description of the review process, see the [Code Review Guidelines](CODE_REVIEW_DOCS.md). ### Reporting Bugs diff --git a/contrib/STYLE.md b/contrib/STYLE.md index daf2a68d32..b7ac755fc0 100644 --- a/contrib/STYLE.md +++ b/contrib/STYLE.md @@ -6,21 +6,58 @@ Most programming languages have well-established conventions as to what constitu # Table of Contents 1. [Code Style](#code-style) -2. [Git Commit Style](#git-commit-style) -3. [The Six Rules of a Great Commit](#the-six-rules-of-a-great-commit) +2. [Naming Conventions](#naming-conventions) +3. [Git Commit Style](#git-commit-style) +4. [The Six Rules of a Great Commit](#the-six-rules-of-a-great-commit) - [1. Atomic Commits](#1-atomic-commits) - [2. Separate Subject from Body with a Blank Line](#2-separate-subject-from-body-with-a-blank-line) - [3. Limit the Subject Line to 50 Characters](#3-limit-the-subject-line-to-50-characters) - [4. Use the Imperative Mood in the Subject Line](#4-use-the-imperative-mood-in-the-subject-line) - [5. Wrap the Body at 72 Characters](#5-wrap-the-body-at-72-characters) - [6. Use the Body to Explain What and Why vs. How](#6-use-the-body-to-explain-what-and-why-vs-how) -4. [Tools Worth Mentioning](#tools-worth-mentioning) +5. [Tools Worth Mentioning](#tools-worth-mentioning) - [Using `--fixup`](#using---fixup) - [Interactive Rebase](#interactive-rebase) -5. [Pull Request and Squashing Commits Caveats](#pull-request-and-squashing-commits-caveats) +6. [Pull Request and Squashing Commits Caveats](#pull-request-and-squashing-commits-caveats) ### Code style + +#### General Style +Python's official style guide is PEP 8, which provides conventions for writing code for the main Python distribution. Here are some key points: + +- `Indentation:` Use 4 spaces per indentation level. + +- `Line Length:` Limit all lines to a maximum of 79 characters. + +- `Blank Lines:` Surround top-level function and class definitions with two blank lines. Method definitions inside a class are surrounded by a single blank line. + +- `Imports:` Imports should usually be on separate lines and should be grouped in the following order: + + - Standard library imports. + - Related third party imports. + - Local application/library specific imports. +- `Whitespace:` Avoid extraneous whitespace in the following situations: + + - Immediately inside parentheses, brackets or braces. + - Immediately before a comma, semicolon, or colon. + - Immediately before the open parenthesis that starts the argument list of a function call. +- `Comments:` Comments should be complete sentences and should be used to clarify code and are not a substitute for poorly written code. + +#### For Python + +- `List Comprehensions:` Use list comprehensions for concise and readable creation of lists. + +- `Generators:` Use generators when dealing with large amounts of data to save memory. + +- `Context Managers:` Use context managers (with statement) for resource management. + +- `String Formatting:` Use f-strings for formatting strings in Python 3.6 and above. + +- `Error Handling:` Use exceptions for error handling whenever possible. + +#### More details + Use `black` to format your python code before commiting for consistency across such a large pool of contributors. Black's code [style](https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#code-style) ensures consistent and opinionated code formatting. It automatically formats your Python code according to the Black style guide, enhancing code readability and maintainability. Key Features of Black: @@ -31,6 +68,18 @@ Key Features of Black: Automation: Black automates the code formatting process, saving time and effort. It eliminates the need for manual formatting and reduces the likelihood of inconsistencies. +### Naming Conventions + +- `Classes:` Class names should normally use the CapWords Convention. +- `Functions and Variables:` Function names should be lowercase, with words separated by underscores as necessary to improve readability. Variable names follow the same convention as function names. + +- `Constants:` Constants are usually defined on a module level and written in all capital letters with underscores separating words. + +- `Non-public Methods and Instance Variables:` Use a single leading underscore (_). This is a weak "internal use" indicator. + +- `Strongly "private" methods and variables:` Use a double leading underscore (__). This triggers name mangling in Python. + + ### Git commit style Here’s a model Git commit message when contributing: diff --git a/requirements/prod.txt b/requirements/prod.txt index e0797f34a6..47970a6b35 100644 --- a/requirements/prod.txt +++ b/requirements/prod.txt @@ -7,7 +7,6 @@ fuzzywuzzy>=0.18.0 fastapi==0.99.1 httpx==0.24.1 loguru==0.7.0 -miniupnpc==2.0.2 munch==2.5.0 netaddr numpy diff --git a/scripts/check_compatibility.sh b/scripts/check_compatibility.sh new file mode 100755 index 0000000000..3a6cf47e4c --- /dev/null +++ b/scripts/check_compatibility.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +if [ -z "$1" ]; then + echo "Please provide a Python version as an argument." + exit 1 +fi + +python_version="$1" +all_passed=true + +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +check_compatibility() { + all_supported=0 + + while read -r requirement; do + package_name=$(echo "$requirement" | awk -F'[!=<>]' '{print $1}' | awk -F'[' '{print $1}') # Strip off brackets + echo -n "Checking $package_name... " + + url="https://pypi.org/pypi/$package_name/json" + response=$(curl -s $url) + status_code=$(curl -s -o /dev/null -w "%{http_code}" $url) + + if [ "$status_code" != "200" ]; then + echo -e "${RED}Information not available for $package_name. Failure.${NC}" + all_supported=1 + continue + fi + + classifiers=$(echo "$response" | jq -r '.info.classifiers[]') + requires_python=$(echo "$response" | jq -r '.info.requires_python') + + base_version="Programming Language :: Python :: ${python_version%%.*}" + specific_version="Programming Language :: Python :: $python_version" + + if echo "$classifiers" | grep -q "$specific_version" || echo "$classifiers" | grep -q "$base_version"; then + echo -e "${GREEN}Supported${NC}" + elif [ "$requires_python" != "null" ]; then + if echo "$requires_python" | grep -Eq "==$python_version|>=$python_version|<=$python_version"; then + echo -e "${GREEN}Supported${NC}" + else + echo -e "${RED}Not compatible with Python $python_version due to constraint $requires_python.${NC}" + all_supported=1 + fi + else + echo -e "${YELLOW}Warning: Specific version not listed, assuming compatibility${NC}" + fi + done < requirements/prod.txt + + return $all_supported +} + +echo "Checking compatibility for Python $python_version..." +check_compatibility +if [ $? -eq 0 ]; then + echo -e "${GREEN}All requirements are compatible with Python $python_version.${NC}" +else + echo -e "${RED}All requirements are NOT compatible with Python $python_version.${NC}" + all_passed=false +fi + +echo "" +if $all_passed; then + echo -e "${GREEN}All tests passed.${NC}" +else + echo -e "${RED}All tests did not pass.${NC}" + exit 1 +fi diff --git a/scripts/check_requirements_changes.sh b/scripts/check_requirements_changes.sh new file mode 100755 index 0000000000..5fcd27ea3f --- /dev/null +++ b/scripts/check_requirements_changes.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Check if requirements files have changed in the last commit +if git diff --name-only HEAD~1 | grep -E 'requirements/prod.txt|requirements/dev.txt'; then + echo "Requirements files have changed. Running compatibility checks..." + echo 'export REQUIREMENTS_CHANGED="true"' >> $BASH_ENV +else + echo "Requirements files have not changed. Skipping compatibility checks..." + echo 'export REQUIREMENTS_CHANGED="false"' >> $BASH_ENV +fi diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index f9632d930b..cddedc9752 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -60,7 +60,6 @@ def test_serve_axon_with_external_ip_set(self): mock_subtensor.serve_axon( netuid=-1, axon=mock_axon_with_external_ip_set, - use_upnpc=False, ) mock_serve_axon.assert_called_once() @@ -115,7 +114,6 @@ def test_serve_axon_with_external_port_set(self): mock_subtensor.serve_axon( netuid=-1, axon=mock_axon_with_external_port_set, - use_upnpc=False, ) mock_serve_axon.assert_called_once() diff --git a/tests/unit_tests/utils/test_networking.py b/tests/unit_tests/utils/test_networking.py index a686a88b06..8e729a8594 100644 --- a/tests/unit_tests/utils/test_networking.py +++ b/tests/unit_tests/utils/test_networking.py @@ -5,7 +5,6 @@ import unittest.mock as mock from bittensor import utils from unittest.mock import MagicMock, PropertyMock -from bittensor.utils.networking import UPNPCException, upnpc_create_port_map # Test conversion functions for IPv4